@notectl/angular 2.0.1 → 2.0.3
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 +258 -121
- package/dist/fesm2022/notectl-angular.mjs +342 -271
- package/dist/fesm2022/notectl-angular.mjs.map +1 -1
- package/dist/types/notectl-angular.d.ts +52 -87
- package/package.json +12 -10
package/README.md
CHANGED
|
@@ -4,12 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
# notectl
|
|
6
6
|
|
|
7
|
-
###
|
|
7
|
+
### Rich text editing as a Web Component
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
React, Vue, Angular, Svelte, or plain HTML. Zero config, full power.
|
|
11
|
-
|
|
12
|
-
<br />
|
|
9
|
+
Build a real editor in plain HTML, React, Vue, Svelte, or Angular without locking yourself into a framework-specific editor runtime.
|
|
13
10
|
|
|
14
11
|
[](https://www.typescriptlang.org/)
|
|
15
12
|
[](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)
|
|
@@ -23,185 +20,325 @@ React, Vue, Angular, Svelte, or plain HTML. Zero config, full power.
|
|
|
23
20
|
|
|
24
21
|
<br />
|
|
25
22
|
|
|
26
|
-
[
|
|
23
|
+
[Documentation](https://samyssmile.github.io/notectl/) ·
|
|
24
|
+
[Playground](https://samyssmile.github.io/notectl/playground/) ·
|
|
25
|
+
[npm: @notectl/core](https://www.npmjs.com/package/@notectl/core) ·
|
|
26
|
+
[npm: @notectl/angular](https://www.npmjs.com/package/@notectl/angular)
|
|
27
27
|
|
|
28
28
|
</div>
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
## What you get
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
- A framework-agnostic editor shipped as the `notectl-editor` custom element
|
|
33
|
+
- Immutable editor state and transaction-based updates
|
|
34
|
+
- A plugin system for headings, lists, links, tables, code blocks, images, fonts, and more
|
|
35
|
+
- CSP-safe styling with `adoptedStyleSheets`
|
|
36
|
+
- A fast path for "just give me a full editor" and a granular path for "I only want these plugins"
|
|
37
|
+
|
|
38
|
+
## Install
|
|
33
39
|
|
|
34
40
|
```bash
|
|
35
41
|
npm install @notectl/core
|
|
36
42
|
```
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
Requirements:
|
|
45
|
+
|
|
46
|
+
- Modern browser with Custom Elements support
|
|
47
|
+
- Node.js 18+ for build tooling
|
|
48
|
+
- Angular 21+ if you use `@notectl/angular`
|
|
49
|
+
|
|
50
|
+
## Quick start
|
|
51
|
+
|
|
52
|
+
The normal way to embed notectl is to create the Web Component with `createEditor(...)` and mount it into your app.
|
|
53
|
+
|
|
54
|
+
### 1. Add a host element
|
|
55
|
+
|
|
56
|
+
```html
|
|
57
|
+
<div id="app"></div>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### 2. Create the editor
|
|
61
|
+
|
|
62
|
+
Start with one of the shipped presets:
|
|
63
|
+
|
|
64
|
+
Minimal preset:
|
|
39
65
|
|
|
40
66
|
```ts
|
|
41
|
-
import { createEditor
|
|
67
|
+
import { createEditor } from '@notectl/core';
|
|
68
|
+
import { createMinimalPreset } from '@notectl/core/presets/minimal';
|
|
42
69
|
|
|
43
70
|
const editor = await createEditor({
|
|
44
|
-
...
|
|
45
|
-
theme: ThemePreset.Light,
|
|
71
|
+
...createMinimalPreset(),
|
|
46
72
|
placeholder: 'Start typing...',
|
|
73
|
+
autofocus: true,
|
|
47
74
|
});
|
|
48
75
|
|
|
49
|
-
document.
|
|
76
|
+
document.getElementById('app')!.appendChild(editor);
|
|
50
77
|
```
|
|
51
78
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
### Custom — pick exactly what you need
|
|
79
|
+
Full preset (toolbar, headings, lists, links, tables, code blocks, images, fonts, and more):
|
|
55
80
|
|
|
56
81
|
```ts
|
|
57
|
-
import {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
} from '@notectl/core';
|
|
82
|
+
import { ThemePreset, createEditor } from '@notectl/core';
|
|
83
|
+
import { STARTER_FONTS } from '@notectl/core/fonts';
|
|
84
|
+
import { ToolbarOverflowBehavior } from '@notectl/core/plugins/toolbar';
|
|
85
|
+
import { createFullPreset } from '@notectl/core/presets/full';
|
|
86
|
+
|
|
87
|
+
const preset = createFullPreset({
|
|
88
|
+
font: { fonts: STARTER_FONTS },
|
|
89
|
+
});
|
|
66
90
|
|
|
67
91
|
const editor = await createEditor({
|
|
92
|
+
...preset,
|
|
93
|
+
toolbar: {
|
|
94
|
+
groups: preset.toolbar,
|
|
95
|
+
overflow: ToolbarOverflowBehavior.Flow,
|
|
96
|
+
},
|
|
68
97
|
theme: ThemePreset.Light,
|
|
69
|
-
toolbar: [
|
|
70
|
-
[new TextFormattingPlugin({ bold: true, italic: true, underline: true })],
|
|
71
|
-
[new HeadingPlugin()],
|
|
72
|
-
[new ListPlugin()],
|
|
73
|
-
[new LinkPlugin(), new TablePlugin()],
|
|
74
|
-
],
|
|
75
98
|
placeholder: 'Start typing...',
|
|
76
99
|
autofocus: true,
|
|
77
100
|
});
|
|
78
101
|
|
|
79
|
-
document.
|
|
102
|
+
document.getElementById('app')!.appendChild(editor);
|
|
80
103
|
```
|
|
81
104
|
|
|
82
|
-
|
|
105
|
+
Use `createMinimalPreset()` when you want a lean starting point. Use `createFullPreset()` when you want the standard toolbar and plugin set immediately, including responsive toolbar overflow.
|
|
106
|
+
|
|
107
|
+
## Add notectl to your app
|
|
108
|
+
|
|
109
|
+
### Plain HTML / Vite / Vanilla JS
|
|
110
|
+
|
|
111
|
+
```html
|
|
112
|
+
<!doctype html>
|
|
113
|
+
<html lang="en">
|
|
114
|
+
<head>
|
|
115
|
+
<meta charset="UTF-8" />
|
|
116
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
117
|
+
<title>My editor</title>
|
|
118
|
+
<style>
|
|
119
|
+
#app {
|
|
120
|
+
max-width: 800px;
|
|
121
|
+
margin: 2rem auto;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
notectl-editor {
|
|
125
|
+
--notectl-content-min-height: 320px;
|
|
126
|
+
}
|
|
127
|
+
</style>
|
|
128
|
+
</head>
|
|
129
|
+
<body>
|
|
130
|
+
<div id="app"></div>
|
|
131
|
+
|
|
132
|
+
<script type="module">
|
|
133
|
+
import { createEditor } from '@notectl/core';
|
|
134
|
+
import { createMinimalPreset } from '@notectl/core/presets/minimal';
|
|
135
|
+
|
|
136
|
+
const editor = await createEditor({
|
|
137
|
+
...createMinimalPreset(),
|
|
138
|
+
placeholder: 'Write something...',
|
|
139
|
+
autofocus: true,
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
document.getElementById('app').appendChild(editor);
|
|
143
|
+
</script>
|
|
144
|
+
</body>
|
|
145
|
+
</html>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### React
|
|
83
149
|
|
|
84
|
-
|
|
150
|
+
```tsx
|
|
151
|
+
import { useEffect, useRef } from 'react';
|
|
152
|
+
import { createEditor, type NotectlEditor } from '@notectl/core';
|
|
85
153
|
|
|
86
|
-
|
|
87
|
-
<
|
|
88
|
-
<
|
|
154
|
+
export function Editor() {
|
|
155
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
156
|
+
const editorRef = useRef<NotectlEditor | null>(null);
|
|
89
157
|
|
|
90
|
-
|
|
158
|
+
useEffect(() => {
|
|
159
|
+
let mounted = true;
|
|
91
160
|
|
|
92
|
-
|
|
161
|
+
createEditor({
|
|
162
|
+
placeholder: 'Start typing...',
|
|
163
|
+
autofocus: true,
|
|
164
|
+
}).then((editor) => {
|
|
165
|
+
if (!mounted || !containerRef.current) return;
|
|
166
|
+
containerRef.current.appendChild(editor);
|
|
167
|
+
editorRef.current = editor;
|
|
168
|
+
});
|
|
93
169
|
|
|
94
|
-
|
|
95
|
-
|
|
170
|
+
return () => {
|
|
171
|
+
mounted = false;
|
|
172
|
+
void editorRef.current?.destroy();
|
|
173
|
+
};
|
|
174
|
+
}, []);
|
|
96
175
|
|
|
97
|
-
|
|
176
|
+
return <div ref={containerRef} />;
|
|
177
|
+
}
|
|
178
|
+
```
|
|
98
179
|
|
|
99
|
-
|
|
180
|
+
Vue and Svelte use the same pattern: create the editor on mount, append it to a host element, and call `destroy()` on unmount.
|
|
100
181
|
|
|
101
|
-
|
|
102
|
-
</tr>
|
|
103
|
-
<tr>
|
|
104
|
-
<td>
|
|
182
|
+
### Angular
|
|
105
183
|
|
|
106
|
-
|
|
184
|
+
Use the Angular wrapper if you want template bindings, forms integration, and DI-based defaults.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npm install @notectl/core @notectl/angular
|
|
188
|
+
```
|
|
107
189
|
|
|
108
|
-
|
|
190
|
+
```ts
|
|
191
|
+
// app.config.ts
|
|
192
|
+
import { type ApplicationConfig } from '@angular/core';
|
|
193
|
+
import { provideNotectl } from '@notectl/angular';
|
|
109
194
|
|
|
110
|
-
|
|
111
|
-
|
|
195
|
+
export const appConfig: ApplicationConfig = {
|
|
196
|
+
providers: [provideNotectl()],
|
|
197
|
+
};
|
|
198
|
+
```
|
|
112
199
|
|
|
113
|
-
|
|
200
|
+
```ts
|
|
201
|
+
// editor.component.ts
|
|
202
|
+
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
|
|
203
|
+
import {
|
|
204
|
+
NotectlEditorComponent,
|
|
205
|
+
type Plugin,
|
|
206
|
+
HeadingPlugin,
|
|
207
|
+
LinkPlugin,
|
|
208
|
+
ListPlugin,
|
|
209
|
+
TextFormattingPlugin,
|
|
210
|
+
ThemePreset,
|
|
211
|
+
} from '@notectl/angular';
|
|
212
|
+
|
|
213
|
+
@Component({
|
|
214
|
+
selector: 'app-editor',
|
|
215
|
+
standalone: true,
|
|
216
|
+
imports: [NotectlEditorComponent],
|
|
217
|
+
template: `
|
|
218
|
+
<ntl-editor
|
|
219
|
+
[toolbar]="toolbar"
|
|
220
|
+
[theme]="theme()"
|
|
221
|
+
[autofocus]="true"
|
|
222
|
+
/>
|
|
223
|
+
`,
|
|
224
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
225
|
+
})
|
|
226
|
+
export class EditorComponent {
|
|
227
|
+
protected readonly theme = signal<ThemePreset>(ThemePreset.Light);
|
|
228
|
+
|
|
229
|
+
protected readonly toolbar: ReadonlyArray<ReadonlyArray<Plugin>> = [
|
|
230
|
+
[new TextFormattingPlugin({ bold: true, italic: true, underline: true })],
|
|
231
|
+
[new HeadingPlugin()],
|
|
232
|
+
[new ListPlugin()],
|
|
233
|
+
[new LinkPlugin()],
|
|
234
|
+
];
|
|
235
|
+
}
|
|
236
|
+
```
|
|
114
237
|
|
|
115
|
-
|
|
238
|
+
Full Angular guide: https://samyssmile.github.io/notectl/guides/angular/
|
|
116
239
|
|
|
117
|
-
|
|
118
|
-
</tr>
|
|
119
|
-
</table>
|
|
240
|
+
## Build your own toolbar
|
|
120
241
|
|
|
121
|
-
|
|
242
|
+
If you only want a small editor, import the plugins you need and group them in the toolbar:
|
|
122
243
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
| **StrikethroughPlugin** | ~~Strikethrough~~ text |
|
|
131
|
-
| **SuperSubPlugin** | Superscript and subscript |
|
|
132
|
-
| **HeadingPlugin** | H1 – H6 headings with block type picker |
|
|
133
|
-
| **BlockquotePlugin** | Block quotes |
|
|
134
|
-
| **ListPlugin** | Bullet, ordered, and checklists |
|
|
135
|
-
| **LinkPlugin** | Hyperlink insertion and editing |
|
|
136
|
-
| **TablePlugin** | Full table support with row/column controls |
|
|
137
|
-
| **CodeBlockPlugin** | Code blocks with syntax highlighting |
|
|
138
|
-
| **ImagePlugin** | Image upload, resize, and drag-and-drop |
|
|
139
|
-
| **TextColorPlugin** | Text color picker |
|
|
140
|
-
| **HighlightPlugin** | Text highlighting / background color |
|
|
141
|
-
| **AlignmentPlugin** | Left, center, right, justify |
|
|
142
|
-
| **FontPlugin** | Font family selection with custom web fonts |
|
|
143
|
-
| **FontSizePlugin** | Configurable font sizes |
|
|
144
|
-
| **HorizontalRulePlugin** | Horizontal dividers |
|
|
145
|
-
| **PrintPlugin** | Print editor content with configurable paper sizes |
|
|
146
|
-
|
|
147
|
-
See the [plugin documentation](https://samyssmile.github.io/notectl/plugins/overview/) for configuration and examples.
|
|
244
|
+
```ts
|
|
245
|
+
import { ThemePreset, createEditor } from '@notectl/core';
|
|
246
|
+
import { HeadingPlugin } from '@notectl/core/plugins/heading';
|
|
247
|
+
import { LinkPlugin } from '@notectl/core/plugins/link';
|
|
248
|
+
import { ListPlugin } from '@notectl/core/plugins/list';
|
|
249
|
+
import { TablePlugin } from '@notectl/core/plugins/table';
|
|
250
|
+
import { TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';
|
|
148
251
|
|
|
149
|
-
|
|
252
|
+
const editor = await createEditor({
|
|
253
|
+
theme: ThemePreset.Light,
|
|
254
|
+
toolbar: [
|
|
255
|
+
[new TextFormattingPlugin({ bold: true, italic: true, underline: true })],
|
|
256
|
+
[new HeadingPlugin()],
|
|
257
|
+
[new ListPlugin()],
|
|
258
|
+
[new LinkPlugin(), new TablePlugin()],
|
|
259
|
+
],
|
|
260
|
+
placeholder: 'Start typing...',
|
|
261
|
+
autofocus: true,
|
|
262
|
+
});
|
|
263
|
+
```
|
|
150
264
|
|
|
151
|
-
|
|
265
|
+
Each inner array is one visible toolbar group.
|
|
152
266
|
|
|
153
|
-
|
|
154
|
-
- **i18n** — 8 languages: English, German, Spanish, French, Chinese, Russian, Arabic, Hindi + auto-detect via `Locale.BROWSER`
|
|
155
|
-
- **Paper sizes** — DIN A4, DIN A5, US Letter, US Legal for WYSIWYG page layout
|
|
156
|
-
- **CSP-compliant** — Style delivery via `adoptedStyleSheets`, no inline styles required
|
|
157
|
-
- **Markdown shortcuts** — `#` → H1, `##` → H2, `-` → bullet list, `1.` → ordered list, `>` → blockquote
|
|
158
|
-
- **Syntax highlighting** — Pluggable highlighter for code blocks
|
|
267
|
+
## Read and write content
|
|
159
268
|
|
|
160
|
-
|
|
269
|
+
```ts
|
|
270
|
+
await editor.setContentHTML('<p>Hello <strong>world</strong></p>');
|
|
161
271
|
|
|
162
|
-
|
|
272
|
+
const html = await editor.getContentHTML();
|
|
273
|
+
const json = editor.getJSON();
|
|
274
|
+
const text = editor.getText();
|
|
275
|
+
const empty = editor.isEmpty();
|
|
276
|
+
```
|
|
163
277
|
|
|
164
|
-
|
|
278
|
+
You can also react to lifecycle and state events:
|
|
165
279
|
|
|
166
280
|
```ts
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
editor.
|
|
172
|
-
|
|
281
|
+
editor.on('ready', () => {
|
|
282
|
+
console.log('Editor is ready');
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
editor.on('stateChange', ({ newState }) => {
|
|
286
|
+
console.log('Document changed:', newState.doc);
|
|
287
|
+
});
|
|
173
288
|
```
|
|
174
289
|
|
|
175
|
-
|
|
290
|
+
## Built-in plugins
|
|
176
291
|
|
|
177
|
-
|
|
292
|
+
notectl ships plugins for:
|
|
178
293
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
294
|
+
- Text formatting
|
|
295
|
+
- Headings
|
|
296
|
+
- Blockquotes
|
|
297
|
+
- Bullet, ordered, and checklist lists
|
|
298
|
+
- Links
|
|
299
|
+
- Tables
|
|
300
|
+
- Code blocks
|
|
301
|
+
- Images
|
|
302
|
+
- Text color and highlight
|
|
303
|
+
- Alignment and text direction
|
|
304
|
+
- Fonts and font sizes
|
|
305
|
+
- Horizontal rules
|
|
306
|
+
- Print layouts
|
|
183
307
|
|
|
184
|
-
|
|
308
|
+
Full plugin reference: https://samyssmile.github.io/notectl/plugins/overview/
|
|
185
309
|
|
|
186
|
-
|
|
310
|
+
## Why teams pick notectl
|
|
187
311
|
|
|
188
|
-
|
|
312
|
+
- Works across frameworks because the editor is a Web Component at the core
|
|
313
|
+
- Strong default path for quick setup, without giving up fine-grained plugin control later
|
|
314
|
+
- CSP-friendly by design, without relying on inline styles
|
|
315
|
+
- Single production dependency: `dompurify`
|
|
316
|
+
- Immutable state and transaction-based updates make behavior predictable and testable
|
|
189
317
|
|
|
190
|
-
|
|
191
|
-
git clone https://github.com/Samyssmile/notectl.git
|
|
192
|
-
cd notectl && pnpm install
|
|
193
|
-
pnpm build # build all packages
|
|
194
|
-
pnpm test # run unit tests
|
|
195
|
-
pnpm test:e2e # run e2e tests
|
|
196
|
-
pnpm lint # lint
|
|
197
|
-
```
|
|
318
|
+
## Examples
|
|
198
319
|
|
|
199
|
-
|
|
320
|
+
- Vanilla example: https://github.com/Samyssmile/notectl/tree/main/examples/vanillajs
|
|
321
|
+
- Angular example: https://github.com/Samyssmile/notectl/tree/main/examples/angular
|
|
200
322
|
|
|
201
|
-
|
|
323
|
+
## Documentation
|
|
202
324
|
|
|
203
|
-
|
|
325
|
+
- Getting started: https://samyssmile.github.io/notectl/getting-started/installation/
|
|
326
|
+
- Quick start: https://samyssmile.github.io/notectl/getting-started/quick-start/
|
|
327
|
+
- Angular guide: https://samyssmile.github.io/notectl/guides/angular/
|
|
328
|
+
- Plugin docs: https://samyssmile.github.io/notectl/plugins/overview/
|
|
329
|
+
- Architecture overview: https://samyssmile.github.io/notectl/architecture/overview/
|
|
204
330
|
|
|
205
|
-
|
|
331
|
+
## Development
|
|
206
332
|
|
|
207
|
-
|
|
333
|
+
```bash
|
|
334
|
+
pnpm install
|
|
335
|
+
pnpm build
|
|
336
|
+
pnpm test
|
|
337
|
+
pnpm test:e2e
|
|
338
|
+
pnpm lint
|
|
339
|
+
pnpm typecheck
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## License
|
|
343
|
+
|
|
344
|
+
MIT
|