@notectl/angular 2.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 +12 -10
- package/dist/fesm2022/notectl-angular.mjs +331 -260
- package/dist/fesm2022/notectl-angular.mjs.map +1 -1
- package/dist/types/notectl-angular.d.ts +52 -87
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -38,10 +38,12 @@ npm install @notectl/core
|
|
|
38
38
|
### Preset — full editor in 5 lines
|
|
39
39
|
|
|
40
40
|
```ts
|
|
41
|
-
import { createEditor,
|
|
41
|
+
import { createEditor, ThemePreset } from '@notectl/core';
|
|
42
|
+
import { STARTER_FONTS } from '@notectl/core/fonts';
|
|
43
|
+
import { createFullPreset } from '@notectl/core/presets';
|
|
42
44
|
|
|
43
45
|
const editor = await createEditor({
|
|
44
|
-
...createFullPreset(),
|
|
46
|
+
...createFullPreset({ font: { fonts: STARTER_FONTS } }),
|
|
45
47
|
theme: ThemePreset.Light,
|
|
46
48
|
placeholder: 'Start typing...',
|
|
47
49
|
});
|
|
@@ -49,7 +51,7 @@ const editor = await createEditor({
|
|
|
49
51
|
document.body.appendChild(editor);
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
All
|
|
54
|
+
All standard plugins, toolbar groups, and keyboard shortcuts — ready to go.
|
|
53
55
|
|
|
54
56
|
### Custom — pick exactly what you need
|
|
55
57
|
|
|
@@ -57,12 +59,12 @@ All 19 plugins, toolbar groups, and keyboard shortcuts — ready to go.
|
|
|
57
59
|
import {
|
|
58
60
|
createEditor,
|
|
59
61
|
ThemePreset,
|
|
60
|
-
TextFormattingPlugin,
|
|
61
|
-
HeadingPlugin,
|
|
62
|
-
ListPlugin,
|
|
63
|
-
LinkPlugin,
|
|
64
|
-
TablePlugin,
|
|
65
62
|
} from '@notectl/core';
|
|
63
|
+
import { HeadingPlugin } from '@notectl/core/plugins/heading';
|
|
64
|
+
import { LinkPlugin } from '@notectl/core/plugins/link';
|
|
65
|
+
import { ListPlugin } from '@notectl/core/plugins/list';
|
|
66
|
+
import { TablePlugin } from '@notectl/core/plugins/table';
|
|
67
|
+
import { TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';
|
|
66
68
|
|
|
67
69
|
const editor = await createEditor({
|
|
68
70
|
theme: ThemePreset.Light,
|
|
@@ -151,7 +153,7 @@ See the [plugin documentation](https://samyssmile.github.io/notectl/plugins/over
|
|
|
151
153
|
## Built-in Features
|
|
152
154
|
|
|
153
155
|
- **Themes** — Dark and Light presets, or create fully custom themes
|
|
154
|
-
- **i18n** —
|
|
156
|
+
- **i18n** — 9 languages: English, German, Spanish, French, Chinese, Russian, Arabic, Hindi, Portuguese + auto-detect via `Locale.BROWSER`
|
|
155
157
|
- **Paper sizes** — DIN A4, DIN A5, US Letter, US Legal for WYSIWYG page layout
|
|
156
158
|
- **CSP-compliant** — Style delivery via `adoptedStyleSheets`, no inline styles required
|
|
157
159
|
- **Markdown shortcuts** — `#` → H1, `##` → H2, `-` → bullet list, `1.` → ordered list, `>` → blockquote
|
|
@@ -165,7 +167,7 @@ Read and write content in any format:
|
|
|
165
167
|
|
|
166
168
|
```ts
|
|
167
169
|
await editor.getContentHTML(); // export as HTML
|
|
168
|
-
editor.setContentHTML('<p>Hello <strong>world</strong></p>'); // import HTML
|
|
170
|
+
await editor.setContentHTML('<p>Hello <strong>world</strong></p>'); // import HTML
|
|
169
171
|
editor.getJSON(); // structured JSON
|
|
170
172
|
editor.setJSON(doc); // import JSON
|
|
171
173
|
editor.getText(); // plain text
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { InjectionToken, makeEnvironmentProviders, inject, DestroyRef, input, model, output, signal, computed, viewChild, afterNextRender, effect, ChangeDetectionStrategy, Component,
|
|
2
|
+
import { InjectionToken, makeEnvironmentProviders, inject, DestroyRef, input, model, output, signal, computed, viewChild, afterNextRender, effect, forwardRef, ChangeDetectionStrategy, Component, Directive, Injectable } from '@angular/core';
|
|
3
|
+
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
3
4
|
import { ThemePreset, NotectlEditor } from '@notectl/core';
|
|
4
5
|
export { DARK_THEME, LIGHT_THEME, ThemePreset, createTheme } from '@notectl/core';
|
|
5
|
-
import { NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
6
6
|
import { Subject } from 'rxjs';
|
|
7
7
|
export { STARTER_FONTS } from '@notectl/core/fonts';
|
|
8
8
|
export { TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';
|
|
@@ -25,6 +25,142 @@ export { TextDirectionPlugin } from '@notectl/core/plugins/text-direction';
|
|
|
25
25
|
export { SuperSubPlugin } from '@notectl/core/plugins/super-sub';
|
|
26
26
|
export { ToolbarPlugin } from '@notectl/core/plugins/toolbar';
|
|
27
27
|
|
|
28
|
+
const EMPTY_HTML = '<p></p>';
|
|
29
|
+
/** Escapes HTML special characters before inserting plain text. */
|
|
30
|
+
function escapeHtml(text) {
|
|
31
|
+
return text
|
|
32
|
+
.replace(/&/g, '&')
|
|
33
|
+
.replace(/</g, '<')
|
|
34
|
+
.replace(/>/g, '>')
|
|
35
|
+
.replace(/"/g, '"')
|
|
36
|
+
.replace(/'/g, ''');
|
|
37
|
+
}
|
|
38
|
+
function isDocumentValue(value) {
|
|
39
|
+
return typeof value === 'object' && value !== null;
|
|
40
|
+
}
|
|
41
|
+
async function readEditorValue(editor, format) {
|
|
42
|
+
switch (format) {
|
|
43
|
+
case 'html':
|
|
44
|
+
return editor.getContentHTML();
|
|
45
|
+
case 'text':
|
|
46
|
+
return editor.getText();
|
|
47
|
+
default:
|
|
48
|
+
return editor.getJSON();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function writeEditorValue(editor, format, value) {
|
|
52
|
+
if (value === null || value === '') {
|
|
53
|
+
await editor.setContentHTML(EMPTY_HTML);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (format === 'json' && isDocumentValue(value)) {
|
|
57
|
+
editor.setJSON(value);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (format === 'text' && typeof value === 'string') {
|
|
61
|
+
await editor.setContentHTML(`<p>${escapeHtml(value)}</p>`);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (typeof value === 'string') {
|
|
65
|
+
await editor.setContentHTML(value);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
editor.setJSON(value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
class EditorValueController {
|
|
72
|
+
options;
|
|
73
|
+
lastDocument;
|
|
74
|
+
mutedControlChanges = 0;
|
|
75
|
+
serializedReadVersion = 0;
|
|
76
|
+
writeQueue = Promise.resolve();
|
|
77
|
+
constructor(options) {
|
|
78
|
+
this.options = options;
|
|
79
|
+
}
|
|
80
|
+
reset() {
|
|
81
|
+
this.lastDocument = undefined;
|
|
82
|
+
this.serializedReadVersion++;
|
|
83
|
+
}
|
|
84
|
+
handleEditorStateChange(doc) {
|
|
85
|
+
this.lastDocument = doc;
|
|
86
|
+
this.options.updateContent(doc);
|
|
87
|
+
if (this.mutedControlChanges > 0)
|
|
88
|
+
return;
|
|
89
|
+
const readVersion = ++this.serializedReadVersion;
|
|
90
|
+
void this.readCurrentValue().then((value) => {
|
|
91
|
+
if (readVersion !== this.serializedReadVersion)
|
|
92
|
+
return;
|
|
93
|
+
this.options.emitControlValue(value);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
syncExternalContent(doc) {
|
|
97
|
+
if (doc === this.lastDocument)
|
|
98
|
+
return;
|
|
99
|
+
this.lastDocument = doc;
|
|
100
|
+
void this.enqueueSilent(async (editor) => {
|
|
101
|
+
editor.setJSON(doc);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
writeControlValue(value) {
|
|
105
|
+
void this.enqueueSilent(async (editor) => {
|
|
106
|
+
await writeEditorValue(editor, this.options.getFormat(), value);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
setDocument(doc) {
|
|
110
|
+
this.lastDocument = doc;
|
|
111
|
+
return this.enqueueInteractive(async (editor) => {
|
|
112
|
+
editor.setJSON(doc);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
setSerializedValue(value) {
|
|
116
|
+
return this.enqueueInteractive(async (editor) => {
|
|
117
|
+
await writeEditorValue(editor, this.options.getFormat(), value);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
applyInitialDocument(editor, doc) {
|
|
121
|
+
this.lastDocument = doc;
|
|
122
|
+
return this.runSilent(async () => {
|
|
123
|
+
editor.setJSON(doc);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async readCurrentValue() {
|
|
127
|
+
const editor = this.options.getEditor();
|
|
128
|
+
if (!editor)
|
|
129
|
+
return null;
|
|
130
|
+
return readEditorValue(editor, this.options.getFormat());
|
|
131
|
+
}
|
|
132
|
+
enqueueSilent(task) {
|
|
133
|
+
return this.enqueue(task, true);
|
|
134
|
+
}
|
|
135
|
+
enqueueInteractive(task) {
|
|
136
|
+
return this.enqueue(task, false);
|
|
137
|
+
}
|
|
138
|
+
enqueue(task, silent) {
|
|
139
|
+
const next = this.writeQueue.then(async () => {
|
|
140
|
+
await this.options.whenReady();
|
|
141
|
+
const editor = this.options.getEditor();
|
|
142
|
+
if (!editor)
|
|
143
|
+
return;
|
|
144
|
+
if (silent) {
|
|
145
|
+
await this.runSilent(() => task(editor));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
await task(editor);
|
|
149
|
+
});
|
|
150
|
+
this.writeQueue = next.catch(() => undefined);
|
|
151
|
+
return next;
|
|
152
|
+
}
|
|
153
|
+
async runSilent(task) {
|
|
154
|
+
this.mutedControlChanges++;
|
|
155
|
+
try {
|
|
156
|
+
await task();
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
this.mutedControlChanges--;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
28
164
|
/**
|
|
29
165
|
* Default configuration applied to all `<notectl-editor>` instances within
|
|
30
166
|
* the injector scope. Component-level inputs override these defaults.
|
|
@@ -65,58 +201,37 @@ function provideNotectl(options = {}) {
|
|
|
65
201
|
return makeEnvironmentProviders(providers);
|
|
66
202
|
}
|
|
67
203
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
* <ntl-editor [toolbar]="toolbar" [plugins]="plugins" />
|
|
78
|
-
*
|
|
79
|
-
* <!-- Two-way content binding -->
|
|
80
|
-
* <ntl-editor [(content)]="myDocument" [toolbar]="toolbar" />
|
|
81
|
-
*
|
|
82
|
-
* <!-- Reactive forms -->
|
|
83
|
-
* <ntl-editor [formControl]="editorControl" [toolbar]="toolbar" />
|
|
84
|
-
* ```
|
|
85
|
-
*/
|
|
204
|
+
function initConfigEquals(a, b) {
|
|
205
|
+
return (a.plugins === b.plugins &&
|
|
206
|
+
a.toolbar === b.toolbar &&
|
|
207
|
+
a.features === b.features &&
|
|
208
|
+
a.autofocus === b.autofocus &&
|
|
209
|
+
a.maxHistoryDepth === b.maxHistoryDepth &&
|
|
210
|
+
a.locale === b.locale &&
|
|
211
|
+
a.styleNonce === b.styleNonce);
|
|
212
|
+
}
|
|
86
213
|
class NotectlEditorComponent {
|
|
87
|
-
// --- Injected dependencies ---
|
|
88
214
|
destroyRef = inject(DestroyRef);
|
|
89
215
|
defaultConfig = inject(NOTECTL_DEFAULT_CONFIG, { optional: true });
|
|
90
|
-
|
|
91
|
-
plugins = input(
|
|
92
|
-
toolbar = input(...(ngDevMode ? [
|
|
93
|
-
features = input(...(ngDevMode ? [
|
|
94
|
-
placeholder = input(
|
|
95
|
-
readonlyMode = input(
|
|
96
|
-
autofocus = input(
|
|
97
|
-
maxHistoryDepth = input(...(ngDevMode ? [
|
|
98
|
-
theme = input(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
* its own state. Once the editor emits changes, the model updates to the
|
|
105
|
-
* current `Document`.
|
|
106
|
-
*
|
|
107
|
-
* @example
|
|
108
|
-
* ```html
|
|
109
|
-
* <ntl-editor [(content)]="myDocument" />
|
|
110
|
-
* ```
|
|
111
|
-
*/
|
|
112
|
-
content = model(...(ngDevMode ? [undefined, { debugName: "content" }] : []));
|
|
113
|
-
// --- Signal Outputs (events bridged from Web Component) ---
|
|
216
|
+
contentFormat = inject(NOTECTL_CONTENT_FORMAT, { optional: true }) ?? 'json';
|
|
217
|
+
plugins = input(undefined, ...(ngDevMode ? [{ debugName: "plugins" }] : []));
|
|
218
|
+
toolbar = input(undefined, ...(ngDevMode ? [{ debugName: "toolbar" }] : []));
|
|
219
|
+
features = input(undefined, ...(ngDevMode ? [{ debugName: "features" }] : []));
|
|
220
|
+
placeholder = input(undefined, ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
|
|
221
|
+
readonlyMode = input(undefined, ...(ngDevMode ? [{ debugName: "readonlyMode" }] : []));
|
|
222
|
+
autofocus = input(undefined, ...(ngDevMode ? [{ debugName: "autofocus" }] : []));
|
|
223
|
+
maxHistoryDepth = input(undefined, ...(ngDevMode ? [{ debugName: "maxHistoryDepth" }] : []));
|
|
224
|
+
theme = input(undefined, ...(ngDevMode ? [{ debugName: "theme" }] : []));
|
|
225
|
+
paperSize = input(undefined, ...(ngDevMode ? [{ debugName: "paperSize" }] : []));
|
|
226
|
+
dir = input(undefined, ...(ngDevMode ? [{ debugName: "dir" }] : []));
|
|
227
|
+
locale = input(undefined, ...(ngDevMode ? [{ debugName: "locale" }] : []));
|
|
228
|
+
styleNonce = input(undefined, ...(ngDevMode ? [{ debugName: "styleNonce" }] : []));
|
|
229
|
+
content = model(undefined, ...(ngDevMode ? [{ debugName: "content" }] : []));
|
|
114
230
|
stateChange = output();
|
|
115
231
|
selectionChange = output();
|
|
116
232
|
editorFocus = output();
|
|
117
233
|
editorBlur = output();
|
|
118
234
|
ready = output();
|
|
119
|
-
// --- Reactive State ---
|
|
120
235
|
editorState = signal(null, ...(ngDevMode ? [{ debugName: "editorState" }] : []));
|
|
121
236
|
isEmpty = computed(() => {
|
|
122
237
|
const state = this.editorState();
|
|
@@ -132,125 +247,146 @@ class NotectlEditorComponent {
|
|
|
132
247
|
return true;
|
|
133
248
|
return block.type === 'paragraph' && block.children.length === 0;
|
|
134
249
|
}, ...(ngDevMode ? [{ debugName: "isEmpty" }] : []));
|
|
135
|
-
|
|
250
|
+
resolvedPlugins = computed(() => this.plugins() ?? this.defaultConfig?.plugins ?? [], ...(ngDevMode ? [{ debugName: "resolvedPlugins" }] : []));
|
|
251
|
+
resolvedToolbar = computed(() => this.toolbar() ?? this.defaultConfig?.toolbar, ...(ngDevMode ? [{ debugName: "resolvedToolbar" }] : []));
|
|
252
|
+
resolvedFeatures = computed(() => this.features() ?? this.defaultConfig?.features, ...(ngDevMode ? [{ debugName: "resolvedFeatures" }] : []));
|
|
253
|
+
resolvedPlaceholder = computed(() => this.placeholder() ?? this.defaultConfig?.placeholder ?? 'Start typing...', ...(ngDevMode ? [{ debugName: "resolvedPlaceholder" }] : []));
|
|
254
|
+
resolvedReadonlyMode = computed(() => this.readonlyMode() ?? this.defaultConfig?.readonly ?? false, ...(ngDevMode ? [{ debugName: "resolvedReadonlyMode" }] : []));
|
|
255
|
+
resolvedAutofocus = computed(() => this.autofocus() ?? this.defaultConfig?.autofocus ?? false, ...(ngDevMode ? [{ debugName: "resolvedAutofocus" }] : []));
|
|
256
|
+
resolvedMaxHistoryDepth = computed(() => this.maxHistoryDepth() ?? this.defaultConfig?.maxHistoryDepth, ...(ngDevMode ? [{ debugName: "resolvedMaxHistoryDepth" }] : []));
|
|
257
|
+
resolvedTheme = computed(() => this.theme() ?? this.defaultConfig?.theme ?? ThemePreset.Light, ...(ngDevMode ? [{ debugName: "resolvedTheme" }] : []));
|
|
258
|
+
resolvedPaperSize = computed(() => this.paperSize() ?? this.defaultConfig?.paperSize, ...(ngDevMode ? [{ debugName: "resolvedPaperSize" }] : []));
|
|
259
|
+
resolvedDir = computed(() => this.dir() ?? this.defaultConfig?.dir, ...(ngDevMode ? [{ debugName: "resolvedDir" }] : []));
|
|
260
|
+
resolvedLocale = computed(() => this.locale() ?? this.defaultConfig?.locale, ...(ngDevMode ? [{ debugName: "resolvedLocale" }] : []));
|
|
261
|
+
resolvedStyleNonce = computed(() => this.styleNonce() ?? this.defaultConfig?.styleNonce, ...(ngDevMode ? [{ debugName: "resolvedStyleNonce" }] : []));
|
|
262
|
+
effectiveReadonly = computed(() => this.disabledByForms() || this.resolvedReadonlyMode(), ...(ngDevMode ? [{ debugName: "effectiveReadonly" }] : []));
|
|
136
263
|
hostRef = viewChild.required('host');
|
|
264
|
+
initialized = signal(false, ...(ngDevMode ? [{ debugName: "initialized" }] : []));
|
|
265
|
+
disabledByForms = signal(false, ...(ngDevMode ? [{ debugName: "disabledByForms" }] : []));
|
|
266
|
+
valueController = new EditorValueController({
|
|
267
|
+
emitControlValue: (value) => this.onChange(value),
|
|
268
|
+
getEditor: () => this.editorRef,
|
|
269
|
+
getFormat: () => this.contentFormat,
|
|
270
|
+
updateContent: (doc) => this.content.set(doc),
|
|
271
|
+
whenReady: () => this.readyPromise,
|
|
272
|
+
});
|
|
137
273
|
editorRef = null;
|
|
138
274
|
readyResolve = null;
|
|
139
|
-
readyPromise
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
275
|
+
readyPromise;
|
|
276
|
+
lastInitConfig = null;
|
|
277
|
+
queuedInitConfig = null;
|
|
278
|
+
reinitializePromise = null;
|
|
279
|
+
pendingInitialDocument;
|
|
280
|
+
onChange = () => { };
|
|
281
|
+
onTouched = () => { };
|
|
145
282
|
constructor() {
|
|
146
|
-
|
|
283
|
+
this.resetReadyPromise();
|
|
147
284
|
afterNextRender(() => {
|
|
148
|
-
this.initEditor();
|
|
285
|
+
this.initEditor(this.captureInitConfig());
|
|
149
286
|
});
|
|
150
|
-
// React to input changes via effect() — replaces ngOnChanges
|
|
151
287
|
effect(() => {
|
|
152
|
-
const currentTheme = this.theme();
|
|
153
|
-
const currentPlaceholder = this.placeholder();
|
|
154
|
-
const currentReadonly = this.readonlyMode();
|
|
155
288
|
const editor = this.editorRef;
|
|
156
289
|
if (!this.initialized() || !editor)
|
|
157
290
|
return;
|
|
158
|
-
editor.setTheme(
|
|
291
|
+
editor.setTheme(this.resolvedTheme());
|
|
159
292
|
editor.configure({
|
|
160
|
-
|
|
161
|
-
|
|
293
|
+
dir: this.resolvedDir(),
|
|
294
|
+
paperSize: this.resolvedPaperSize(),
|
|
295
|
+
placeholder: this.resolvedPlaceholder(),
|
|
296
|
+
readonly: this.effectiveReadonly(),
|
|
162
297
|
});
|
|
163
298
|
});
|
|
164
|
-
// Sync external content model changes into the editor.
|
|
165
|
-
// Skips when the document was set by the editor itself (feedback loop prevention).
|
|
166
299
|
effect(() => {
|
|
167
300
|
const doc = this.content();
|
|
168
|
-
|
|
169
|
-
if (!this.initialized() || !editor || !doc)
|
|
170
|
-
return;
|
|
171
|
-
// Skip if this document originated from the editor's own state change
|
|
172
|
-
if (doc === this.lastEditorDoc)
|
|
301
|
+
if (!doc)
|
|
173
302
|
return;
|
|
174
|
-
|
|
303
|
+
this.valueController.syncExternalContent(doc);
|
|
304
|
+
});
|
|
305
|
+
effect(() => {
|
|
306
|
+
this.scheduleReinitialization(this.captureInitConfig());
|
|
175
307
|
});
|
|
176
308
|
this.destroyRef.onDestroy(() => {
|
|
177
|
-
this.destroyEditor();
|
|
309
|
+
void this.destroyEditor();
|
|
178
310
|
});
|
|
179
311
|
}
|
|
180
|
-
// --- Public API (proxy to Web Component) ---
|
|
181
|
-
/** Returns the document as JSON. */
|
|
182
312
|
getJSON() {
|
|
183
313
|
return this.requireEditor().getJSON();
|
|
184
314
|
}
|
|
185
|
-
/** Sets the document from JSON. */
|
|
186
315
|
setJSON(doc) {
|
|
187
|
-
this.
|
|
316
|
+
void this.valueController.setDocument(doc);
|
|
188
317
|
}
|
|
189
|
-
/** Returns sanitized HTML representation of the document. */
|
|
190
318
|
async getContentHTML() {
|
|
191
319
|
return this.requireEditor().getContentHTML();
|
|
192
320
|
}
|
|
193
|
-
/** Sets content from HTML (sanitized). */
|
|
194
321
|
async setContentHTML(html) {
|
|
195
|
-
|
|
322
|
+
await this.valueController.setSerializedValue(html);
|
|
196
323
|
}
|
|
197
|
-
/** Returns plain text content. */
|
|
198
324
|
getText() {
|
|
199
325
|
return this.requireEditor().getText();
|
|
200
326
|
}
|
|
201
|
-
/** Proxy to the Web Component's `commands` object. */
|
|
202
327
|
get commands() {
|
|
203
328
|
return this.requireEditor().commands;
|
|
204
329
|
}
|
|
205
|
-
/** Proxy to the Web Component's `can()` capability checker. */
|
|
206
330
|
can() {
|
|
207
331
|
return this.requireEditor().can();
|
|
208
332
|
}
|
|
209
|
-
/** Executes a named command registered by a plugin. */
|
|
210
333
|
executeCommand(name) {
|
|
211
|
-
|
|
212
|
-
return false;
|
|
213
|
-
return this.editorRef.executeCommand(name);
|
|
334
|
+
return this.editorRef?.executeCommand(name) ?? false;
|
|
214
335
|
}
|
|
215
|
-
/** Configures a plugin at runtime. */
|
|
216
336
|
configurePlugin(pluginId, config) {
|
|
217
337
|
this.editorRef?.configurePlugin(pluginId, config);
|
|
218
338
|
}
|
|
219
|
-
/** Dispatches a transaction. */
|
|
220
339
|
dispatch(tr) {
|
|
221
340
|
this.editorRef?.dispatch(tr);
|
|
222
341
|
}
|
|
223
|
-
/** Returns the current editor state. */
|
|
224
342
|
getState() {
|
|
225
343
|
return this.requireEditor().getState();
|
|
226
344
|
}
|
|
227
|
-
/** Changes the theme at runtime. */
|
|
228
345
|
setTheme(theme) {
|
|
229
346
|
this.editorRef?.setTheme(theme);
|
|
230
347
|
}
|
|
231
|
-
/** Returns the current theme setting. */
|
|
232
348
|
getTheme() {
|
|
233
|
-
return this.editorRef?.getTheme() ?? this.
|
|
349
|
+
return this.editorRef?.getTheme() ?? this.resolvedTheme();
|
|
234
350
|
}
|
|
235
|
-
/** Sets the readonly state programmatically (used by ControlValueAccessor). */
|
|
236
351
|
setReadonly(readonlyState) {
|
|
237
352
|
this.editorRef?.configure({ readonly: readonlyState });
|
|
238
353
|
}
|
|
239
|
-
/** Resolves when the editor is ready. */
|
|
240
354
|
whenReady() {
|
|
241
355
|
return this.readyPromise;
|
|
242
356
|
}
|
|
243
|
-
|
|
244
|
-
|
|
357
|
+
focus(options) {
|
|
358
|
+
const editor = this.editorRef;
|
|
359
|
+
if (!editor)
|
|
360
|
+
return;
|
|
361
|
+
const focusTarget = editor.shadowRoot?.querySelector('[contenteditable]') ?? editor;
|
|
362
|
+
focusTarget.focus(options);
|
|
363
|
+
}
|
|
364
|
+
writeValue(value) {
|
|
365
|
+
this.valueController.writeControlValue(value);
|
|
366
|
+
}
|
|
367
|
+
registerOnChange(fn) {
|
|
368
|
+
this.onChange = fn;
|
|
369
|
+
}
|
|
370
|
+
registerOnTouched(fn) {
|
|
371
|
+
this.onTouched = fn;
|
|
372
|
+
}
|
|
373
|
+
setDisabledState(isDisabled) {
|
|
374
|
+
this.disabledByForms.set(isDisabled);
|
|
375
|
+
}
|
|
376
|
+
resetReadyPromise() {
|
|
377
|
+
this.readyPromise = new Promise((resolve) => {
|
|
378
|
+
this.readyResolve = resolve;
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
initEditor(snapshot) {
|
|
245
382
|
const hostElement = this.hostRef().nativeElement;
|
|
246
|
-
const config = this.buildConfig();
|
|
247
383
|
const editor = new NotectlEditor();
|
|
248
384
|
this.editorRef = editor;
|
|
249
|
-
|
|
250
|
-
|
|
385
|
+
this.lastInitConfig = snapshot;
|
|
386
|
+
this.valueController.reset();
|
|
251
387
|
editor.on('stateChange', (event) => {
|
|
252
388
|
this.editorState.set(event.newState);
|
|
253
|
-
this.
|
|
389
|
+
this.valueController.handleEditorStateChange(event.newState.doc);
|
|
254
390
|
this.stateChange.emit(event);
|
|
255
391
|
});
|
|
256
392
|
editor.on('selectionChange', (event) => {
|
|
@@ -260,211 +396,146 @@ class NotectlEditorComponent {
|
|
|
260
396
|
this.editorFocus.emit();
|
|
261
397
|
});
|
|
262
398
|
editor.on('blur', () => {
|
|
399
|
+
this.onTouched();
|
|
263
400
|
this.editorBlur.emit();
|
|
264
401
|
});
|
|
265
402
|
editor.on('ready', () => {
|
|
266
403
|
this.initialized.set(true);
|
|
267
|
-
|
|
268
|
-
this.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
editor.setJSON(initialContent);
|
|
273
|
-
}
|
|
274
|
-
this.readyResolve?.();
|
|
275
|
-
this.ready.emit();
|
|
404
|
+
this.editorState.set(editor.getState());
|
|
405
|
+
void this.applyInitialContent(editor).finally(() => {
|
|
406
|
+
this.readyResolve?.();
|
|
407
|
+
this.ready.emit();
|
|
408
|
+
});
|
|
276
409
|
});
|
|
277
|
-
|
|
278
|
-
// connectedCallback which calls init() without config — by calling
|
|
279
|
-
// init(config) first, the editor initializes with the full config
|
|
280
|
-
// and connectedCallback's init() becomes a no-op (already initialized).
|
|
281
|
-
editor.init(config);
|
|
410
|
+
editor.init(this.buildConfig());
|
|
282
411
|
hostElement.appendChild(editor);
|
|
283
412
|
}
|
|
413
|
+
async applyInitialContent(editor) {
|
|
414
|
+
const currentContent = this.content();
|
|
415
|
+
const initialContent = currentContent ?? this.pendingInitialDocument;
|
|
416
|
+
this.pendingInitialDocument = undefined;
|
|
417
|
+
if (!initialContent)
|
|
418
|
+
return;
|
|
419
|
+
await this.valueController.applyInitialDocument(editor, initialContent);
|
|
420
|
+
}
|
|
284
421
|
buildConfig() {
|
|
285
|
-
const defaults = this.defaultConfig ?? {};
|
|
286
|
-
const toolbar = this.toolbar();
|
|
287
|
-
const features = this.features();
|
|
288
|
-
const maxHistory = this.maxHistoryDepth();
|
|
289
422
|
const config = {
|
|
290
|
-
...
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
423
|
+
...this.defaultConfig,
|
|
424
|
+
autofocus: this.resolvedAutofocus(),
|
|
425
|
+
dir: this.resolvedDir(),
|
|
426
|
+
locale: this.resolvedLocale(),
|
|
427
|
+
maxHistoryDepth: this.resolvedMaxHistoryDepth(),
|
|
428
|
+
paperSize: this.resolvedPaperSize(),
|
|
429
|
+
placeholder: this.resolvedPlaceholder(),
|
|
430
|
+
plugins: this.resolvedPlugins(),
|
|
431
|
+
readonly: this.effectiveReadonly(),
|
|
432
|
+
styleNonce: this.resolvedStyleNonce(),
|
|
433
|
+
theme: this.resolvedTheme(),
|
|
296
434
|
};
|
|
435
|
+
const toolbar = this.resolvedToolbar();
|
|
297
436
|
if (toolbar !== undefined) {
|
|
298
437
|
config.toolbar = toolbar;
|
|
299
438
|
}
|
|
439
|
+
const features = this.resolvedFeatures();
|
|
300
440
|
if (features !== undefined) {
|
|
301
441
|
config.features = features;
|
|
302
442
|
}
|
|
303
|
-
if (maxHistory !== undefined) {
|
|
304
|
-
config.maxHistoryDepth = maxHistory;
|
|
305
|
-
}
|
|
306
443
|
return config;
|
|
307
444
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
445
|
+
captureInitConfig() {
|
|
446
|
+
return {
|
|
447
|
+
autofocus: this.resolvedAutofocus(),
|
|
448
|
+
features: this.resolvedFeatures(),
|
|
449
|
+
locale: this.resolvedLocale(),
|
|
450
|
+
maxHistoryDepth: this.resolvedMaxHistoryDepth(),
|
|
451
|
+
plugins: this.resolvedPlugins(),
|
|
452
|
+
styleNonce: this.resolvedStyleNonce(),
|
|
453
|
+
toolbar: this.resolvedToolbar(),
|
|
454
|
+
};
|
|
312
455
|
}
|
|
313
|
-
|
|
314
|
-
if (this.
|
|
315
|
-
|
|
316
|
-
|
|
456
|
+
scheduleReinitialization(snapshot) {
|
|
457
|
+
if (!this.initialized() || !this.lastInitConfig)
|
|
458
|
+
return;
|
|
459
|
+
if (initConfigEquals(snapshot, this.lastInitConfig))
|
|
460
|
+
return;
|
|
461
|
+
this.queuedInitConfig = snapshot;
|
|
462
|
+
if (this.reinitializePromise)
|
|
463
|
+
return;
|
|
464
|
+
this.reinitializePromise = (async () => {
|
|
465
|
+
while (this.queuedInitConfig) {
|
|
466
|
+
const nextSnapshot = this.queuedInitConfig;
|
|
467
|
+
this.queuedInitConfig = null;
|
|
468
|
+
this.pendingInitialDocument = this.editorRef?.getJSON();
|
|
469
|
+
this.resetReadyPromise();
|
|
470
|
+
this.initialized.set(false);
|
|
471
|
+
await this.destroyEditor();
|
|
472
|
+
this.initEditor(nextSnapshot);
|
|
473
|
+
await this.readyPromise;
|
|
474
|
+
}
|
|
475
|
+
})().finally(() => {
|
|
476
|
+
this.reinitializePromise = null;
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
async destroyEditor() {
|
|
480
|
+
const editor = this.editorRef;
|
|
481
|
+
if (!editor) {
|
|
482
|
+
this.initialized.set(false);
|
|
483
|
+
this.editorState.set(null);
|
|
484
|
+
return;
|
|
317
485
|
}
|
|
486
|
+
this.editorRef = null;
|
|
487
|
+
editor.remove();
|
|
488
|
+
await editor.destroy();
|
|
318
489
|
this.initialized.set(false);
|
|
490
|
+
this.editorState.set(null);
|
|
319
491
|
}
|
|
320
492
|
requireEditor() {
|
|
321
493
|
const editor = this.editorRef;
|
|
322
|
-
if (!editor) {
|
|
494
|
+
if (!editor || !this.initialized()) {
|
|
323
495
|
throw new Error('NotectlEditor is not initialized. Await whenReady() first.');
|
|
324
496
|
}
|
|
325
497
|
return editor;
|
|
326
498
|
}
|
|
327
499
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NotectlEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
328
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.0", type: NotectlEditorComponent, isStandalone: true, selector: "ntl-editor", inputs: { plugins: { classPropertyName: "plugins", publicName: "plugins", isSignal: true, isRequired: false, transformFunction: null }, toolbar: { classPropertyName: "toolbar", publicName: "toolbar", isSignal: true, isRequired: false, transformFunction: null }, features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonlyMode: { classPropertyName: "readonlyMode", publicName: "readonlyMode", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, maxHistoryDepth: { classPropertyName: "maxHistoryDepth", publicName: "maxHistoryDepth", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null },
|
|
500
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.0", type: NotectlEditorComponent, isStandalone: true, selector: "ntl-editor", inputs: { plugins: { classPropertyName: "plugins", publicName: "plugins", isSignal: true, isRequired: false, transformFunction: null }, toolbar: { classPropertyName: "toolbar", publicName: "toolbar", isSignal: true, isRequired: false, transformFunction: null }, features: { classPropertyName: "features", publicName: "features", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonlyMode: { classPropertyName: "readonlyMode", publicName: "readonlyMode", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, maxHistoryDepth: { classPropertyName: "maxHistoryDepth", publicName: "maxHistoryDepth", isSignal: true, isRequired: false, transformFunction: null }, theme: { classPropertyName: "theme", publicName: "theme", isSignal: true, isRequired: false, transformFunction: null }, paperSize: { classPropertyName: "paperSize", publicName: "paperSize", isSignal: true, isRequired: false, transformFunction: null }, dir: { classPropertyName: "dir", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, styleNonce: { classPropertyName: "styleNonce", publicName: "styleNonce", isSignal: true, isRequired: false, transformFunction: null }, content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { content: "contentChange", stateChange: "stateChange", selectionChange: "selectionChange", editorFocus: "editorFocus", editorBlur: "editorBlur", ready: "ready" }, host: { properties: { "attr.aria-disabled": "effectiveReadonly() ? \"true\" : null", "class.ntl-editor-disabled": "effectiveReadonly()" } }, providers: [
|
|
501
|
+
{
|
|
502
|
+
provide: NG_VALUE_ACCESSOR,
|
|
503
|
+
useExisting: forwardRef(() => NotectlEditorComponent),
|
|
504
|
+
multi: true,
|
|
505
|
+
},
|
|
506
|
+
], viewQueries: [{ propertyName: "hostRef", first: true, predicate: ["host"], descendants: true, isSignal: true }], ngImport: i0, template: '<div #host></div>', isInline: true, styles: [":host{display:block}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
329
507
|
}
|
|
330
508
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NotectlEditorComponent, decorators: [{
|
|
331
509
|
type: Component,
|
|
332
|
-
args: [{ selector: 'ntl-editor', template: '<div #host></div>', changeDetection: ChangeDetectionStrategy.OnPush,
|
|
333
|
-
|
|
510
|
+
args: [{ selector: 'ntl-editor', standalone: true, template: '<div #host></div>', changeDetection: ChangeDetectionStrategy.OnPush, providers: [
|
|
511
|
+
{
|
|
512
|
+
provide: NG_VALUE_ACCESSOR,
|
|
513
|
+
useExisting: forwardRef(() => NotectlEditorComponent),
|
|
514
|
+
multi: true,
|
|
515
|
+
},
|
|
516
|
+
], host: {
|
|
517
|
+
'[attr.aria-disabled]': 'effectiveReadonly() ? "true" : null',
|
|
518
|
+
'[class.ntl-editor-disabled]': 'effectiveReadonly()',
|
|
519
|
+
}, styles: [":host{display:block}\n"] }]
|
|
520
|
+
}], ctorParameters: () => [], propDecorators: { plugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "plugins", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], features: [{ type: i0.Input, args: [{ isSignal: true, alias: "features", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], readonlyMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonlyMode", required: false }] }], autofocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "autofocus", required: false }] }], maxHistoryDepth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHistoryDepth", required: false }] }], theme: [{ type: i0.Input, args: [{ isSignal: true, alias: "theme", required: false }] }], paperSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "paperSize", required: false }] }], dir: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }], locale: [{ type: i0.Input, args: [{ isSignal: true, alias: "locale", required: false }] }], styleNonce: [{ type: i0.Input, args: [{ isSignal: true, alias: "styleNonce", required: false }] }], content: [{ type: i0.Input, args: [{ isSignal: true, alias: "content", required: false }] }, { type: i0.Output, args: ["contentChange"] }], stateChange: [{ type: i0.Output, args: ["stateChange"] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }], editorFocus: [{ type: i0.Output, args: ["editorFocus"] }], editorBlur: [{ type: i0.Output, args: ["editorBlur"] }], ready: [{ type: i0.Output, args: ["ready"] }], hostRef: [{ type: i0.ViewChild, args: ['host', { isSignal: true }] }] } });
|
|
334
521
|
|
|
335
|
-
/** Escapes HTML special characters to prevent XSS when inserting plain text. */
|
|
336
|
-
function escapeHtml(text) {
|
|
337
|
-
return text
|
|
338
|
-
.replace(/&/g, '&')
|
|
339
|
-
.replace(/</g, '<')
|
|
340
|
-
.replace(/>/g, '>')
|
|
341
|
-
.replace(/"/g, '"')
|
|
342
|
-
.replace(/'/g, ''');
|
|
343
|
-
}
|
|
344
522
|
/**
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
* Supports Reactive Forms (`formControl`, `formControlName`) and
|
|
348
|
-
* template-driven forms (`ngModel`).
|
|
523
|
+
* @deprecated Angular Forms support is built into `NotectlEditorComponent`.
|
|
349
524
|
*
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
* - `'json'` (default) — form value is a `Document` object
|
|
353
|
-
* - `'html'` — form value is a sanitized HTML string
|
|
354
|
-
* - `'text'` — form value is a plain text string
|
|
525
|
+
* This directive remains as a compatibility shim so existing imports do not break,
|
|
526
|
+
* but it no longer participates in value accessor registration.
|
|
355
527
|
*/
|
|
356
528
|
class NotectlValueAccessorDirective {
|
|
357
|
-
editor = inject(NotectlEditorComponent);
|
|
358
|
-
destroyRef = inject(DestroyRef);
|
|
359
|
-
format = inject(NOTECTL_CONTENT_FORMAT, { optional: true }) ?? 'json';
|
|
360
|
-
onChange = () => { };
|
|
361
|
-
onTouched = () => { };
|
|
362
|
-
pendingValue = null;
|
|
363
|
-
suppressEmit = false;
|
|
364
|
-
stateChangeSub = this.editor.stateChange.subscribe((_event) => {
|
|
365
|
-
if (this.suppressEmit)
|
|
366
|
-
return;
|
|
367
|
-
void this.readValue().then((value) => {
|
|
368
|
-
this.onChange(value);
|
|
369
|
-
});
|
|
370
|
-
});
|
|
371
|
-
blurSub = this.editor.editorBlur.subscribe(() => {
|
|
372
|
-
this.onTouched();
|
|
373
|
-
});
|
|
374
|
-
constructor() {
|
|
375
|
-
this.destroyRef.onDestroy(() => {
|
|
376
|
-
this.stateChangeSub.unsubscribe();
|
|
377
|
-
this.blurSub.unsubscribe();
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
writeValue(value) {
|
|
381
|
-
if (!value)
|
|
382
|
-
return;
|
|
383
|
-
try {
|
|
384
|
-
this.editor.getState();
|
|
385
|
-
void this.writeValueToEditor(value);
|
|
386
|
-
return;
|
|
387
|
-
}
|
|
388
|
-
catch {
|
|
389
|
-
// Editor not ready yet — defer
|
|
390
|
-
}
|
|
391
|
-
this.pendingValue = value;
|
|
392
|
-
void this.editor.whenReady().then(async () => {
|
|
393
|
-
if (this.pendingValue !== null) {
|
|
394
|
-
await this.writeValueToEditor(this.pendingValue);
|
|
395
|
-
this.pendingValue = null;
|
|
396
|
-
}
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
registerOnChange(fn) {
|
|
400
|
-
this.onChange = fn;
|
|
401
|
-
}
|
|
402
|
-
registerOnTouched(fn) {
|
|
403
|
-
this.onTouched = fn;
|
|
404
|
-
}
|
|
405
|
-
setDisabledState(isDisabled) {
|
|
406
|
-
this.editor.setReadonly(isDisabled);
|
|
407
|
-
}
|
|
408
|
-
async writeValueToEditor(value) {
|
|
409
|
-
this.suppressEmit = true;
|
|
410
|
-
try {
|
|
411
|
-
if (this.format === 'json' && typeof value === 'object') {
|
|
412
|
-
this.editor.setJSON(value);
|
|
413
|
-
}
|
|
414
|
-
else if (this.format === 'html' && typeof value === 'string') {
|
|
415
|
-
await this.editor.setContentHTML(value);
|
|
416
|
-
}
|
|
417
|
-
else if (this.format === 'text' && typeof value === 'string') {
|
|
418
|
-
await this.editor.setContentHTML(`<p>${escapeHtml(value)}</p>`);
|
|
419
|
-
}
|
|
420
|
-
else if (typeof value === 'string') {
|
|
421
|
-
await this.editor.setContentHTML(value);
|
|
422
|
-
}
|
|
423
|
-
else {
|
|
424
|
-
this.editor.setJSON(value);
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
finally {
|
|
428
|
-
this.suppressEmit = false;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
async readValue() {
|
|
432
|
-
try {
|
|
433
|
-
switch (this.format) {
|
|
434
|
-
case 'html':
|
|
435
|
-
return await this.editor.getContentHTML();
|
|
436
|
-
case 'text':
|
|
437
|
-
return this.editor.getText();
|
|
438
|
-
default:
|
|
439
|
-
return this.editor.getJSON();
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
catch {
|
|
443
|
-
return null;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
529
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NotectlValueAccessorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
447
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: NotectlValueAccessorDirective, isStandalone: true, selector: "ntl-editor[formControl], ntl-editor[formControlName], ntl-editor[ngModel]",
|
|
448
|
-
{
|
|
449
|
-
provide: NG_VALUE_ACCESSOR,
|
|
450
|
-
useExisting: forwardRef(() => NotectlValueAccessorDirective),
|
|
451
|
-
multi: true,
|
|
452
|
-
},
|
|
453
|
-
], ngImport: i0 });
|
|
530
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: NotectlValueAccessorDirective, isStandalone: true, selector: "ntl-editor[formControl], ntl-editor[formControlName], ntl-editor[ngModel]", ngImport: i0 });
|
|
454
531
|
}
|
|
455
532
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NotectlValueAccessorDirective, decorators: [{
|
|
456
533
|
type: Directive,
|
|
457
534
|
args: [{
|
|
458
535
|
selector: 'ntl-editor[formControl], ntl-editor[formControlName], ntl-editor[ngModel]',
|
|
459
|
-
|
|
460
|
-
{
|
|
461
|
-
provide: NG_VALUE_ACCESSOR,
|
|
462
|
-
useExisting: forwardRef(() => NotectlValueAccessorDirective),
|
|
463
|
-
multi: true,
|
|
464
|
-
},
|
|
465
|
-
],
|
|
536
|
+
standalone: true,
|
|
466
537
|
}]
|
|
467
|
-
}]
|
|
538
|
+
}] });
|
|
468
539
|
|
|
469
540
|
/**
|
|
470
541
|
* Optional injectable service for programmatic editor access via DI.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notectl-angular.mjs","sources":["../../src/lib/tokens.ts","../../src/lib/notectl-editor.component.ts","../../src/lib/value-accessor.directive.ts","../../src/lib/notectl-editor.service.ts","../../src/public-api.ts","../../src/notectl-angular.ts"],"sourcesContent":["import { type EnvironmentProviders, InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport type { NotectlEditorConfig } from '@notectl/core';\n\n/**\n * Default configuration applied to all `<notectl-editor>` instances within\n * the injector scope. Component-level inputs override these defaults.\n */\nexport const NOTECTL_DEFAULT_CONFIG: InjectionToken<Partial<NotectlEditorConfig>> =\n\tnew InjectionToken<Partial<NotectlEditorConfig>>('notectl-default-config');\n\n/**\n * Content format used by the `ControlValueAccessor` for forms integration.\n * Determines how editor content is serialized when reading/writing form values.\n *\n * - `'json'` — `Document` objects (default)\n * - `'html'` — sanitized HTML strings\n * - `'text'` — plain text strings\n */\nexport const NOTECTL_CONTENT_FORMAT: InjectionToken<ContentFormat> =\n\tnew InjectionToken<ContentFormat>('notectl-content-format');\n\n/** Supported content serialization formats for forms integration. */\nexport type ContentFormat = 'json' | 'html' | 'text';\n\n/** Options for `provideNotectl()`. */\nexport interface NotectlProviderOptions {\n\t/** Default editor configuration applied to all instances. */\n\treadonly config?: Partial<NotectlEditorConfig>;\n\t/** Content serialization format for forms integration. Defaults to `'json'`. */\n\treadonly contentFormat?: ContentFormat;\n}\n\n/**\n * Configures the notectl editor at the environment injector level.\n *\n * @example\n * ```typescript\n * bootstrapApplication(App, {\n * providers: [\n * provideNotectl({\n * config: { theme: ThemePreset.Light, placeholder: 'Start typing...' },\n * contentFormat: 'json',\n * }),\n * ],\n * });\n * ```\n */\nexport function provideNotectl(options: NotectlProviderOptions = {}): EnvironmentProviders {\n\tconst providers = [];\n\n\tif (options.config) {\n\t\tproviders.push({ provide: NOTECTL_DEFAULT_CONFIG, useValue: options.config });\n\t}\n\n\tif (options.contentFormat) {\n\t\tproviders.push({ provide: NOTECTL_CONTENT_FORMAT, useValue: options.contentFormat });\n\t}\n\n\treturn makeEnvironmentProviders(providers);\n}\n","import {\n\tChangeDetectionStrategy,\n\tComponent,\n\tDestroyRef,\n\ttype ElementRef,\n\ttype ModelSignal,\n\tafterNextRender,\n\tcomputed,\n\teffect,\n\tinject,\n\tinput,\n\tmodel,\n\toutput,\n\tsignal,\n\tviewChild,\n} from '@angular/core';\nimport type {\n\tDocument,\n\tEditorSelection,\n\tEditorState,\n\tNotectlEditorConfig,\n\tPlugin,\n\tPluginConfig,\n\tStateChangeEvent,\n\tTheme,\n\tTransaction,\n} from '@notectl/core';\nimport { NotectlEditor, ThemePreset } from '@notectl/core';\nimport type { TextFormattingConfig } from '@notectl/core/plugins/text-formatting';\n\nimport { NOTECTL_DEFAULT_CONFIG } from './tokens';\nimport type { SelectionChangeEvent } from './types';\n\n/**\n * Angular standalone component wrapping the `<ntl-editor>` Web Component.\n *\n * Uses `afterNextRender()` for SSR-safe initialization and `effect()` for\n * reactive input tracking — no lifecycle interfaces needed.\n *\n * @example\n * ```html\n * <!-- Basic usage -->\n * <ntl-editor [toolbar]=\"toolbar\" [plugins]=\"plugins\" />\n *\n * <!-- Two-way content binding -->\n * <ntl-editor [(content)]=\"myDocument\" [toolbar]=\"toolbar\" />\n *\n * <!-- Reactive forms -->\n * <ntl-editor [formControl]=\"editorControl\" [toolbar]=\"toolbar\" />\n * ```\n */\n@Component({\n\tselector: 'ntl-editor',\n\ttemplate: '<div #host></div>',\n\tstyles: ':host { display: block; }',\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class NotectlEditorComponent {\n\t// --- Injected dependencies ---\n\n\tprivate readonly destroyRef: DestroyRef = inject(DestroyRef);\n\tprivate readonly defaultConfig: Partial<NotectlEditorConfig> | null = inject(\n\t\tNOTECTL_DEFAULT_CONFIG,\n\t\t{ optional: true },\n\t);\n\n\t// --- Signal Inputs (1:1 with Web Component config) ---\n\n\treadonly plugins = input<Plugin[]>([]);\n\treadonly toolbar = input<ReadonlyArray<ReadonlyArray<Plugin>>>();\n\treadonly features = input<Partial<TextFormattingConfig>>();\n\treadonly placeholder = input<string>('Start typing...');\n\treadonly readonlyMode = input<boolean>(false);\n\treadonly autofocus = input<boolean>(false);\n\treadonly maxHistoryDepth = input<number>();\n\treadonly theme = input<ThemePreset | Theme>(ThemePreset.Light);\n\n\t// --- Two-way content binding via model() ---\n\n\t/**\n\t * Two-way bindable document content.\n\t *\n\t * `undefined` means no external content was provided — the editor manages\n\t * its own state. Once the editor emits changes, the model updates to the\n\t * current `Document`.\n\t *\n\t * @example\n\t * ```html\n\t * <ntl-editor [(content)]=\"myDocument\" />\n\t * ```\n\t */\n\treadonly content: ModelSignal<Document | undefined> = model<Document>();\n\n\t// --- Signal Outputs (events bridged from Web Component) ---\n\n\treadonly stateChange = output<StateChangeEvent>();\n\treadonly selectionChange = output<SelectionChangeEvent>();\n\treadonly editorFocus = output<void>();\n\treadonly editorBlur = output<void>();\n\treadonly ready = output<void>();\n\n\t// --- Reactive State ---\n\n\treadonly editorState = signal<EditorState | null>(null);\n\n\treadonly isEmpty = computed<boolean>(() => {\n\t\tconst state: EditorState | null = this.editorState();\n\t\tif (!state) return true;\n\t\tconst doc: Document = state.doc;\n\t\tif (doc.children.length === 0) return true;\n\t\tif (doc.children.length > 1) return false;\n\t\tconst block = doc.children[0];\n\t\tif (!block) return true;\n\t\treturn block.type === 'paragraph' && block.children.length === 0;\n\t});\n\n\t// --- Internal state ---\n\n\tprivate readonly hostRef = viewChild.required<ElementRef<HTMLDivElement>>('host');\n\tprivate editorRef: NotectlEditor | null = null;\n\tprivate readyResolve: (() => void) | null = null;\n\tprivate readonly readyPromise: Promise<void> = new Promise<void>((resolve) => {\n\t\tthis.readyResolve = resolve;\n\t});\n\tprivate readonly initialized = signal(false);\n\n\t/** Tracks the last document set from within the editor to prevent feedback loops. */\n\tprivate lastEditorDoc: Document | null = null;\n\n\tconstructor() {\n\t\t// SSR-safe: only runs in the browser after first render\n\t\tafterNextRender(() => {\n\t\t\tthis.initEditor();\n\t\t});\n\n\t\t// React to input changes via effect() — replaces ngOnChanges\n\t\teffect(() => {\n\t\t\tconst currentTheme: ThemePreset | Theme = this.theme();\n\t\t\tconst currentPlaceholder: string = this.placeholder();\n\t\t\tconst currentReadonly: boolean = this.readonlyMode();\n\n\t\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\t\tif (!this.initialized() || !editor) return;\n\n\t\t\teditor.setTheme(currentTheme);\n\t\t\teditor.configure({\n\t\t\t\tplaceholder: currentPlaceholder,\n\t\t\t\treadonly: currentReadonly,\n\t\t\t});\n\t\t});\n\n\t\t// Sync external content model changes into the editor.\n\t\t// Skips when the document was set by the editor itself (feedback loop prevention).\n\t\teffect(() => {\n\t\t\tconst doc: Document | undefined = this.content();\n\t\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\t\tif (!this.initialized() || !editor || !doc) return;\n\n\t\t\t// Skip if this document originated from the editor's own state change\n\t\t\tif (doc === this.lastEditorDoc) return;\n\n\t\t\teditor.setJSON(doc);\n\t\t});\n\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.destroyEditor();\n\t\t});\n\t}\n\n\t// --- Public API (proxy to Web Component) ---\n\n\t/** Returns the document as JSON. */\n\tgetJSON(): Document {\n\t\treturn this.requireEditor().getJSON();\n\t}\n\n\t/** Sets the document from JSON. */\n\tsetJSON(doc: Document): void {\n\t\tthis.requireEditor().setJSON(doc);\n\t}\n\n\t/** Returns sanitized HTML representation of the document. */\n\tasync getContentHTML(): Promise<string> {\n\t\treturn this.requireEditor().getContentHTML();\n\t}\n\n\t/** Sets content from HTML (sanitized). */\n\tasync setContentHTML(html: string): Promise<void> {\n\t\treturn this.requireEditor().setContentHTML(html);\n\t}\n\n\t/** Returns plain text content. */\n\tgetText(): string {\n\t\treturn this.requireEditor().getText();\n\t}\n\n\t/** Proxy to the Web Component's `commands` object. */\n\tget commands(): NotectlEditor['commands'] {\n\t\treturn this.requireEditor().commands;\n\t}\n\n\t/** Proxy to the Web Component's `can()` capability checker. */\n\tcan(): ReturnType<NotectlEditor['can']> {\n\t\treturn this.requireEditor().can();\n\t}\n\n\t/** Executes a named command registered by a plugin. */\n\texecuteCommand(name: string): boolean {\n\t\tif (!this.editorRef) return false;\n\t\treturn this.editorRef.executeCommand(name);\n\t}\n\n\t/** Configures a plugin at runtime. */\n\tconfigurePlugin(pluginId: string, config: PluginConfig): void {\n\t\tthis.editorRef?.configurePlugin(pluginId, config);\n\t}\n\n\t/** Dispatches a transaction. */\n\tdispatch(tr: Transaction): void {\n\t\tthis.editorRef?.dispatch(tr);\n\t}\n\n\t/** Returns the current editor state. */\n\tgetState(): EditorState {\n\t\treturn this.requireEditor().getState();\n\t}\n\n\t/** Changes the theme at runtime. */\n\tsetTheme(theme: ThemePreset | Theme): void {\n\t\tthis.editorRef?.setTheme(theme);\n\t}\n\n\t/** Returns the current theme setting. */\n\tgetTheme(): ThemePreset | Theme {\n\t\treturn this.editorRef?.getTheme() ?? this.theme();\n\t}\n\n\t/** Sets the readonly state programmatically (used by ControlValueAccessor). */\n\tsetReadonly(readonlyState: boolean): void {\n\t\tthis.editorRef?.configure({ readonly: readonlyState });\n\t}\n\n\t/** Resolves when the editor is ready. */\n\twhenReady(): Promise<void> {\n\t\treturn this.readyPromise;\n\t}\n\n\t// --- Private ---\n\n\tprivate initEditor(): void {\n\t\tconst hostElement: HTMLDivElement = this.hostRef().nativeElement;\n\t\tconst config: NotectlEditorConfig = this.buildConfig();\n\n\t\tconst editor: NotectlEditor = new NotectlEditor();\n\t\tthis.editorRef = editor;\n\n\t\t// Register event listeners BEFORE appending to DOM, because\n\t\t// appendChild triggers connectedCallback → init() synchronously.\n\t\teditor.on('stateChange', (event: StateChangeEvent) => {\n\t\t\tthis.editorState.set(event.newState);\n\t\t\tthis.syncContentModel(event.newState.doc);\n\t\t\tthis.stateChange.emit(event);\n\t\t});\n\n\t\teditor.on('selectionChange', (event: { selection: EditorSelection }) => {\n\t\t\tthis.selectionChange.emit(event);\n\t\t});\n\n\t\teditor.on('focus', () => {\n\t\t\tthis.editorFocus.emit();\n\t\t});\n\n\t\teditor.on('blur', () => {\n\t\t\tthis.editorBlur.emit();\n\t\t});\n\n\t\teditor.on('ready', () => {\n\t\t\tthis.initialized.set(true);\n\t\t\tconst state: EditorState = editor.getState();\n\t\t\tthis.editorState.set(state);\n\n\t\t\t// Apply initial content model if set before editor was ready\n\t\t\tconst initialContent: Document | undefined = this.content();\n\t\t\tif (initialContent) {\n\t\t\t\teditor.setJSON(initialContent);\n\t\t\t}\n\n\t\t\tthis.readyResolve?.();\n\t\t\tthis.ready.emit();\n\t\t});\n\n\t\t// Init with config BEFORE appending to DOM. appendChild triggers\n\t\t// connectedCallback which calls init() without config — by calling\n\t\t// init(config) first, the editor initializes with the full config\n\t\t// and connectedCallback's init() becomes a no-op (already initialized).\n\t\teditor.init(config);\n\t\thostElement.appendChild(editor);\n\t}\n\n\tprivate buildConfig(): NotectlEditorConfig {\n\t\tconst defaults: Partial<NotectlEditorConfig> = this.defaultConfig ?? {};\n\t\tconst toolbar: ReadonlyArray<ReadonlyArray<Plugin>> | undefined = this.toolbar();\n\t\tconst features: Partial<TextFormattingConfig> | undefined = this.features();\n\t\tconst maxHistory: number | undefined = this.maxHistoryDepth();\n\n\t\tconst config: NotectlEditorConfig = {\n\t\t\t...defaults,\n\t\t\tplugins: this.plugins(),\n\t\t\tplaceholder: this.placeholder(),\n\t\t\treadonly: this.readonlyMode(),\n\t\t\tautofocus: this.autofocus(),\n\t\t\ttheme: this.theme(),\n\t\t};\n\n\t\tif (toolbar !== undefined) {\n\t\t\tconfig.toolbar = toolbar;\n\t\t}\n\t\tif (features !== undefined) {\n\t\t\tconfig.features = features;\n\t\t}\n\t\tif (maxHistory !== undefined) {\n\t\t\tconfig.maxHistoryDepth = maxHistory;\n\t\t}\n\n\t\treturn config;\n\t}\n\n\t/** Syncs editor document changes to the content model without feedback loops. */\n\tprivate syncContentModel(doc: Document): void {\n\t\tthis.lastEditorDoc = doc;\n\t\tthis.content.set(doc);\n\t}\n\n\tprivate destroyEditor(): void {\n\t\tif (this.editorRef) {\n\t\t\tthis.editorRef.destroy();\n\t\t\tthis.editorRef = null;\n\t\t}\n\t\tthis.initialized.set(false);\n\t}\n\n\tprivate requireEditor(): NotectlEditor {\n\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\tif (!editor) {\n\t\t\tthrow new Error('NotectlEditor is not initialized. Await whenReady() first.');\n\t\t}\n\t\treturn editor;\n\t}\n}\n","import { DestroyRef, Directive, forwardRef, inject } from '@angular/core';\nimport { type ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport type { Document, StateChangeEvent } from '@notectl/core';\n\nimport { NotectlEditorComponent } from './notectl-editor.component';\nimport { type ContentFormat, NOTECTL_CONTENT_FORMAT } from './tokens';\n\ntype OnChangeFn = (value: Document | string | null) => void;\ntype OnTouchedFn = () => void;\n\n/** Escapes HTML special characters to prevent XSS when inserting plain text. */\nfunction escapeHtml(text: string): string {\n\treturn text\n\t\t.replace(/&/g, '&')\n\t\t.replace(/</g, '<')\n\t\t.replace(/>/g, '>')\n\t\t.replace(/\"/g, '"')\n\t\t.replace(/'/g, ''');\n}\n\n/**\n * `ControlValueAccessor` directive for Angular forms integration.\n *\n * Supports Reactive Forms (`formControl`, `formControlName`) and\n * template-driven forms (`ngModel`).\n *\n * The content format is configurable via `provideNotectl({ contentFormat })` or\n * the `NOTECTL_CONTENT_FORMAT` injection token:\n * - `'json'` (default) — form value is a `Document` object\n * - `'html'` — form value is a sanitized HTML string\n * - `'text'` — form value is a plain text string\n */\n@Directive({\n\tselector: 'ntl-editor[formControl], ntl-editor[formControlName], ntl-editor[ngModel]',\n\tproviders: [\n\t\t{\n\t\t\tprovide: NG_VALUE_ACCESSOR,\n\t\t\tuseExisting: forwardRef(() => NotectlValueAccessorDirective),\n\t\t\tmulti: true,\n\t\t},\n\t],\n})\nexport class NotectlValueAccessorDirective implements ControlValueAccessor {\n\tprivate readonly editor: NotectlEditorComponent = inject(NotectlEditorComponent);\n\tprivate readonly destroyRef: DestroyRef = inject(DestroyRef);\n\tprivate readonly format: ContentFormat =\n\t\tinject(NOTECTL_CONTENT_FORMAT, { optional: true }) ?? 'json';\n\n\tprivate onChange: OnChangeFn = () => {};\n\tprivate onTouched: OnTouchedFn = () => {};\n\tprivate pendingValue: Document | string | null = null;\n\tprivate suppressEmit = false;\n\n\tprivate readonly stateChangeSub = this.editor.stateChange.subscribe(\n\t\t(_event: StateChangeEvent) => {\n\t\t\tif (this.suppressEmit) return;\n\t\t\tvoid this.readValue().then((value: Document | string | null) => {\n\t\t\t\tthis.onChange(value);\n\t\t\t});\n\t\t},\n\t);\n\n\tprivate readonly blurSub = this.editor.editorBlur.subscribe(() => {\n\t\tthis.onTouched();\n\t});\n\n\tconstructor() {\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.stateChangeSub.unsubscribe();\n\t\t\tthis.blurSub.unsubscribe();\n\t\t});\n\t}\n\n\twriteValue(value: Document | string | null): void {\n\t\tif (!value) return;\n\n\t\ttry {\n\t\t\tthis.editor.getState();\n\t\t\tvoid this.writeValueToEditor(value);\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// Editor not ready yet — defer\n\t\t}\n\n\t\tthis.pendingValue = value;\n\t\tvoid this.editor.whenReady().then(async () => {\n\t\t\tif (this.pendingValue !== null) {\n\t\t\t\tawait this.writeValueToEditor(this.pendingValue);\n\t\t\t\tthis.pendingValue = null;\n\t\t\t}\n\t\t});\n\t}\n\n\tregisterOnChange(fn: OnChangeFn): void {\n\t\tthis.onChange = fn;\n\t}\n\n\tregisterOnTouched(fn: OnTouchedFn): void {\n\t\tthis.onTouched = fn;\n\t}\n\n\tsetDisabledState(isDisabled: boolean): void {\n\t\tthis.editor.setReadonly(isDisabled);\n\t}\n\n\tprivate async writeValueToEditor(value: Document | string): Promise<void> {\n\t\tthis.suppressEmit = true;\n\t\ttry {\n\t\t\tif (this.format === 'json' && typeof value === 'object') {\n\t\t\t\tthis.editor.setJSON(value as Document);\n\t\t\t} else if (this.format === 'html' && typeof value === 'string') {\n\t\t\t\tawait this.editor.setContentHTML(value);\n\t\t\t} else if (this.format === 'text' && typeof value === 'string') {\n\t\t\t\tawait this.editor.setContentHTML(`<p>${escapeHtml(value)}</p>`);\n\t\t\t} else if (typeof value === 'string') {\n\t\t\t\tawait this.editor.setContentHTML(value);\n\t\t\t} else {\n\t\t\t\tthis.editor.setJSON(value as Document);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.suppressEmit = false;\n\t\t}\n\t}\n\n\tprivate async readValue(): Promise<Document | string | null> {\n\t\ttry {\n\t\t\tswitch (this.format) {\n\t\t\t\tcase 'html':\n\t\t\t\t\treturn await this.editor.getContentHTML();\n\t\t\t\tcase 'text':\n\t\t\t\t\treturn this.editor.getText();\n\t\t\t\tdefault:\n\t\t\t\t\treturn this.editor.getJSON();\n\t\t\t}\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n}\n","import { DestroyRef, Injectable, computed, inject, signal } from '@angular/core';\nimport type { EditorState, StateChangeEvent, Transaction } from '@notectl/core';\nimport { type Observable, Subject } from 'rxjs';\n\nimport type { NotectlEditorComponent } from './notectl-editor.component';\n\n/**\n * Optional injectable service for programmatic editor access via DI.\n *\n * Useful when a toolbar or other component needs to interact with\n * the editor without a direct template reference.\n *\n * The service must be provided at a shared injector level and\n * connected to the editor component via `register()`.\n *\n * @example\n * ```typescript\n * @Component({\n * providers: [NotectlEditorService],\n * template: `\n * <app-toolbar />\n * <ntl-editor #editor [plugins]=\"plugins\" />\n * `,\n * })\n * export class EditorPage {\n * private readonly editor = viewChild.required<NotectlEditorComponent>('editor');\n * private readonly service = inject(NotectlEditorService);\n *\n * constructor() {\n * afterNextRender(() => {\n * this.service.register(this.editor());\n * });\n * }\n * }\n * ```\n */\n@Injectable()\nexport class NotectlEditorService {\n\tprivate readonly destroyRef: DestroyRef = inject(DestroyRef);\n\tprivate readonly editorRef = signal<NotectlEditorComponent | null>(null);\n\tprivate readonly stateChangeSubject = new Subject<StateChangeEvent>();\n\n\t/** Whether an editor is currently registered with this service. */\n\treadonly hasEditor = computed<boolean>(() => this.editorRef() !== null);\n\n\t/** Observable stream of state change events from the editor. */\n\treadonly stateChanges$: Observable<StateChangeEvent> = this.stateChangeSubject.asObservable();\n\n\tprivate stateChangeSub: { unsubscribe(): void } | null = null;\n\n\tconstructor() {\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.unregister();\n\t\t\tthis.stateChangeSubject.complete();\n\t\t});\n\t}\n\n\t/** Registers an editor component with this service. */\n\tregister(editor: NotectlEditorComponent): void {\n\t\tthis.unregister();\n\t\tthis.editorRef.set(editor);\n\n\t\tthis.stateChangeSub = editor.stateChange.subscribe((event: StateChangeEvent) => {\n\t\t\tthis.stateChangeSubject.next(event);\n\t\t});\n\t}\n\n\t/** Unregisters the current editor from this service. */\n\tunregister(): void {\n\t\tthis.stateChangeSub?.unsubscribe();\n\t\tthis.stateChangeSub = null;\n\t\tthis.editorRef.set(null);\n\t}\n\n\t/** Executes a named command on the registered editor. */\n\texecuteCommand(name: string): boolean {\n\t\tconst editor: NotectlEditorComponent | null = this.editorRef();\n\t\tif (!editor) return false;\n\t\treturn editor.executeCommand(name);\n\t}\n\n\t/** Returns the current editor state, or `null` if no editor is registered. */\n\tgetState(): EditorState | null {\n\t\tconst editor: NotectlEditorComponent | null = this.editorRef();\n\t\tif (!editor) return null;\n\t\ttry {\n\t\t\treturn editor.getState();\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/** Dispatches a transaction on the registered editor. */\n\tdispatch(tr: Transaction): void {\n\t\tthis.editorRef()?.dispatch(tr);\n\t}\n}\n","/**\n * @notectl/angular — Angular integration for the notectl rich text editor.\n * @packageDocumentation\n */\n\n// --- Angular Bindings ---\nexport { NotectlEditorComponent } from './lib/notectl-editor.component';\nexport { NotectlValueAccessorDirective } from './lib/value-accessor.directive';\nexport { NotectlEditorService } from './lib/notectl-editor.service';\n\n// --- Provider Function ---\nexport {\n\tprovideNotectl,\n\ttype NotectlProviderOptions,\n} from './lib/tokens';\n\n// --- Injection Tokens ---\nexport {\n\tNOTECTL_DEFAULT_CONFIG,\n\tNOTECTL_CONTENT_FORMAT,\n\ttype ContentFormat,\n} from './lib/tokens';\n\n// --- Angular-specific Types ---\nexport type { SelectionChangeEvent } from './lib/types';\n\n// --- Re-exports from @notectl/core (convenience) ---\n\n// Model types\nexport type {\n\tDocument,\n\tBlockNode,\n\tTextNode,\n\tInlineNode,\n\tMark,\n\tBlockAttrs,\n} from '@notectl/core';\n\n// Selection types\nexport type { EditorSelection, Position, Selection } from '@notectl/core';\n\n// State types\nexport type {\n\tEditorState,\n\tTransaction,\n\tTransactionMetadata,\n\tStateChangeEvent,\n} from '@notectl/core';\n\n// Plugin types\nexport type { Plugin, PluginConfig, PluginContext } from '@notectl/core';\n\n// Theme types\nexport type { Theme, PartialTheme, ThemePrimitives } from '@notectl/core';\nexport { ThemePreset, LIGHT_THEME, DARK_THEME, createTheme } from '@notectl/core';\n\n// Editor config\nexport type { NotectlEditorConfig } from '@notectl/core';\n\n// Plugin config types (from sub-path exports)\nexport type { TextFormattingConfig } from '@notectl/core/plugins/text-formatting';\nexport type { FontDefinition } from '@notectl/core/plugins/font';\n\n// Starter fonts\n/** @deprecated Import from '@notectl/core/fonts' instead. */\nexport { STARTER_FONTS } from '@notectl/core/fonts';\n\n// Plugins (tree-shakable re-exports from sub-paths)\nexport { TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';\nexport { HeadingPlugin } from '@notectl/core/plugins/heading';\nexport { ListPlugin } from '@notectl/core/plugins/list';\nexport { LinkPlugin } from '@notectl/core/plugins/link';\nexport { BlockquotePlugin } from '@notectl/core/plugins/blockquote';\nexport { CodeBlockPlugin } from '@notectl/core/plugins/code-block';\nexport { TablePlugin } from '@notectl/core/plugins/table';\nexport { ImagePlugin } from '@notectl/core/plugins/image';\nexport { HorizontalRulePlugin } from '@notectl/core/plugins/horizontal-rule';\nexport { HardBreakPlugin } from '@notectl/core/plugins/hard-break';\nexport { StrikethroughPlugin } from '@notectl/core/plugins/strikethrough';\nexport { HighlightPlugin } from '@notectl/core/plugins/highlight';\nexport { TextColorPlugin } from '@notectl/core/plugins/text-color';\nexport { FontPlugin } from '@notectl/core/plugins/font';\nexport { FontSizePlugin } from '@notectl/core/plugins/font-size';\nexport { AlignmentPlugin } from '@notectl/core/plugins/alignment';\nexport { TextDirectionPlugin } from '@notectl/core/plugins/text-direction';\nexport { SuperSubPlugin } from '@notectl/core/plugins/super-sub';\nexport { ToolbarPlugin } from '@notectl/core/plugins/toolbar';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"notectl-angular.mjs","sources":["../../src/lib/value-interop.ts","../../src/lib/EditorValueController.ts","../../src/lib/tokens.ts","../../src/lib/notectl-editor.component.ts","../../src/lib/value-accessor.directive.ts","../../src/lib/notectl-editor.service.ts","../../src/public-api.ts","../../src/notectl-angular.ts"],"sourcesContent":["import type { Document } from '@notectl/core';\n\nimport type { ContentFormat } from './tokens';\nimport type { NotectlValue } from './types';\n\ninterface EditorContentApi {\n\tgetContentHTML(): Promise<string>;\n\tgetJSON(): Document;\n\tgetText(): string;\n\tsetContentHTML(html: string): Promise<void>;\n\tsetJSON(doc: Document): void;\n}\n\nconst EMPTY_HTML = '<p></p>';\n\n/** Escapes HTML special characters before inserting plain text. */\nfunction escapeHtml(text: string): string {\n\treturn text\n\t\t.replace(/&/g, '&')\n\t\t.replace(/</g, '<')\n\t\t.replace(/>/g, '>')\n\t\t.replace(/\"/g, '"')\n\t\t.replace(/'/g, ''');\n}\n\nfunction isDocumentValue(value: NotectlValue): value is Document {\n\treturn typeof value === 'object' && value !== null;\n}\n\nexport async function readEditorValue(\n\teditor: EditorContentApi,\n\tformat: ContentFormat,\n): Promise<NotectlValue> {\n\tswitch (format) {\n\t\tcase 'html':\n\t\t\treturn editor.getContentHTML();\n\t\tcase 'text':\n\t\t\treturn editor.getText();\n\t\tdefault:\n\t\t\treturn editor.getJSON();\n\t}\n}\n\nexport async function writeEditorValue(\n\teditor: EditorContentApi,\n\tformat: ContentFormat,\n\tvalue: NotectlValue,\n): Promise<void> {\n\tif (value === null || value === '') {\n\t\tawait editor.setContentHTML(EMPTY_HTML);\n\t\treturn;\n\t}\n\n\tif (format === 'json' && isDocumentValue(value)) {\n\t\teditor.setJSON(value);\n\t\treturn;\n\t}\n\n\tif (format === 'text' && typeof value === 'string') {\n\t\tawait editor.setContentHTML(`<p>${escapeHtml(value)}</p>`);\n\t\treturn;\n\t}\n\n\tif (typeof value === 'string') {\n\t\tawait editor.setContentHTML(value);\n\t\treturn;\n\t}\n\n\teditor.setJSON(value);\n}\n","import type { Document, NotectlEditor } from '@notectl/core';\n\nimport type { ContentFormat } from './tokens';\nimport type { NotectlValue } from './types';\nimport { readEditorValue, writeEditorValue } from './value-interop';\n\ninterface EditorValueControllerOptions {\n\treadonly getEditor: () => NotectlEditor | null;\n\treadonly getFormat: () => ContentFormat;\n\treadonly whenReady: () => Promise<void>;\n\treadonly updateContent: (doc: Document) => void;\n\treadonly emitControlValue: (value: NotectlValue) => void;\n}\n\nexport class EditorValueController {\n\tprivate readonly options: EditorValueControllerOptions;\n\tprivate lastDocument: Document | undefined;\n\tprivate mutedControlChanges = 0;\n\tprivate serializedReadVersion = 0;\n\tprivate writeQueue: Promise<void> = Promise.resolve();\n\n\tconstructor(options: EditorValueControllerOptions) {\n\t\tthis.options = options;\n\t}\n\n\treset(): void {\n\t\tthis.lastDocument = undefined;\n\t\tthis.serializedReadVersion++;\n\t}\n\n\thandleEditorStateChange(doc: Document): void {\n\t\tthis.lastDocument = doc;\n\t\tthis.options.updateContent(doc);\n\n\t\tif (this.mutedControlChanges > 0) return;\n\n\t\tconst readVersion = ++this.serializedReadVersion;\n\t\tvoid this.readCurrentValue().then((value: NotectlValue) => {\n\t\t\tif (readVersion !== this.serializedReadVersion) return;\n\t\t\tthis.options.emitControlValue(value);\n\t\t});\n\t}\n\n\tsyncExternalContent(doc: Document): void {\n\t\tif (doc === this.lastDocument) return;\n\t\tthis.lastDocument = doc;\n\t\tvoid this.enqueueSilent(async (editor: NotectlEditor) => {\n\t\t\teditor.setJSON(doc);\n\t\t});\n\t}\n\n\twriteControlValue(value: NotectlValue): void {\n\t\tvoid this.enqueueSilent(async (editor: NotectlEditor) => {\n\t\t\tawait writeEditorValue(editor, this.options.getFormat(), value);\n\t\t});\n\t}\n\n\tsetDocument(doc: Document): Promise<void> {\n\t\tthis.lastDocument = doc;\n\t\treturn this.enqueueInteractive(async (editor: NotectlEditor) => {\n\t\t\teditor.setJSON(doc);\n\t\t});\n\t}\n\n\tsetSerializedValue(value: NotectlValue): Promise<void> {\n\t\treturn this.enqueueInteractive(async (editor: NotectlEditor) => {\n\t\t\tawait writeEditorValue(editor, this.options.getFormat(), value);\n\t\t});\n\t}\n\n\tapplyInitialDocument(editor: NotectlEditor, doc: Document): Promise<void> {\n\t\tthis.lastDocument = doc;\n\t\treturn this.runSilent(async () => {\n\t\t\teditor.setJSON(doc);\n\t\t});\n\t}\n\n\tprivate async readCurrentValue(): Promise<NotectlValue> {\n\t\tconst editor: NotectlEditor | null = this.options.getEditor();\n\t\tif (!editor) return null;\n\t\treturn readEditorValue(editor, this.options.getFormat());\n\t}\n\n\tprivate enqueueSilent(task: (editor: NotectlEditor) => Promise<void>): Promise<void> {\n\t\treturn this.enqueue(task, true);\n\t}\n\n\tprivate enqueueInteractive(task: (editor: NotectlEditor) => Promise<void>): Promise<void> {\n\t\treturn this.enqueue(task, false);\n\t}\n\n\tprivate enqueue(task: (editor: NotectlEditor) => Promise<void>, silent: boolean): Promise<void> {\n\t\tconst next = this.writeQueue.then(async () => {\n\t\t\tawait this.options.whenReady();\n\t\t\tconst editor: NotectlEditor | null = this.options.getEditor();\n\t\t\tif (!editor) return;\n\n\t\t\tif (silent) {\n\t\t\t\tawait this.runSilent(() => task(editor));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tawait task(editor);\n\t\t});\n\n\t\tthis.writeQueue = next.catch(() => undefined);\n\t\treturn next;\n\t}\n\n\tprivate async runSilent(task: () => Promise<void>): Promise<void> {\n\t\tthis.mutedControlChanges++;\n\t\ttry {\n\t\t\tawait task();\n\t\t} finally {\n\t\t\tthis.mutedControlChanges--;\n\t\t}\n\t}\n}\n","import { type EnvironmentProviders, InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport type { NotectlEditorConfig } from '@notectl/core';\n\n/**\n * Default configuration applied to all `<notectl-editor>` instances within\n * the injector scope. Component-level inputs override these defaults.\n */\nexport const NOTECTL_DEFAULT_CONFIG: InjectionToken<Partial<NotectlEditorConfig>> =\n\tnew InjectionToken<Partial<NotectlEditorConfig>>('notectl-default-config');\n\n/**\n * Content format used by the `ControlValueAccessor` for forms integration.\n * Determines how editor content is serialized when reading/writing form values.\n *\n * - `'json'` — `Document` objects (default)\n * - `'html'` — sanitized HTML strings\n * - `'text'` — plain text strings\n */\nexport const NOTECTL_CONTENT_FORMAT: InjectionToken<ContentFormat> =\n\tnew InjectionToken<ContentFormat>('notectl-content-format');\n\n/** Supported content serialization formats for forms integration. */\nexport type ContentFormat = 'json' | 'html' | 'text';\n\n/** Options for `provideNotectl()`. */\nexport interface NotectlProviderOptions {\n\t/** Default editor configuration applied to all instances. */\n\treadonly config?: Partial<NotectlEditorConfig>;\n\t/** Content serialization format for forms integration. Defaults to `'json'`. */\n\treadonly contentFormat?: ContentFormat;\n}\n\n/**\n * Configures the notectl editor at the environment injector level.\n *\n * @example\n * ```typescript\n * bootstrapApplication(App, {\n * providers: [\n * provideNotectl({\n * config: { theme: ThemePreset.Light, placeholder: 'Start typing...' },\n * contentFormat: 'json',\n * }),\n * ],\n * });\n * ```\n */\nexport function provideNotectl(options: NotectlProviderOptions = {}): EnvironmentProviders {\n\tconst providers = [];\n\n\tif (options.config) {\n\t\tproviders.push({ provide: NOTECTL_DEFAULT_CONFIG, useValue: options.config });\n\t}\n\n\tif (options.contentFormat) {\n\t\tproviders.push({ provide: NOTECTL_CONTENT_FORMAT, useValue: options.contentFormat });\n\t}\n\n\treturn makeEnvironmentProviders(providers);\n}\n","import {\n\tChangeDetectionStrategy,\n\tComponent,\n\tDestroyRef,\n\ttype ElementRef,\n\ttype ModelSignal,\n\tafterNextRender,\n\tcomputed,\n\teffect,\n\tforwardRef,\n\tinject,\n\tinput,\n\tmodel,\n\toutput,\n\tsignal,\n\tviewChild,\n} from '@angular/core';\nimport { type ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\nimport type {\n\tDocument,\n\tEditorSelection,\n\tEditorState,\n\tNotectlEditorConfig,\n\tPaperSize,\n\tPlugin,\n\tPluginConfig,\n\tStateChangeEvent,\n\tTheme,\n\tTransaction,\n} from '@notectl/core';\nimport { NotectlEditor, ThemePreset } from '@notectl/core';\nimport type { Locale } from '@notectl/core';\nimport type { TextFormattingConfig } from '@notectl/core/plugins/text-formatting';\n\nimport { EditorValueController } from './EditorValueController';\nimport { type ContentFormat, NOTECTL_CONTENT_FORMAT, NOTECTL_DEFAULT_CONFIG } from './tokens';\nimport type { NotectlValue, SelectionChangeEvent } from './types';\n\ninterface InitConfigSnapshot {\n\treadonly autofocus: boolean;\n\treadonly features: Partial<TextFormattingConfig> | undefined;\n\treadonly locale: Locale | undefined;\n\treadonly maxHistoryDepth: number | undefined;\n\treadonly plugins: readonly Plugin[];\n\treadonly styleNonce: string | undefined;\n\treadonly toolbar: NotectlEditorConfig['toolbar'];\n}\n\nfunction initConfigEquals(a: InitConfigSnapshot, b: InitConfigSnapshot): boolean {\n\treturn (\n\t\ta.plugins === b.plugins &&\n\t\ta.toolbar === b.toolbar &&\n\t\ta.features === b.features &&\n\t\ta.autofocus === b.autofocus &&\n\t\ta.maxHistoryDepth === b.maxHistoryDepth &&\n\t\ta.locale === b.locale &&\n\t\ta.styleNonce === b.styleNonce\n\t);\n}\n\n@Component({\n\tselector: 'ntl-editor',\n\tstandalone: true,\n\ttemplate: '<div #host></div>',\n\tstyles: ':host { display: block; }',\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n\tproviders: [\n\t\t{\n\t\t\tprovide: NG_VALUE_ACCESSOR,\n\t\t\tuseExisting: forwardRef(() => NotectlEditorComponent),\n\t\t\tmulti: true,\n\t\t},\n\t],\n\thost: {\n\t\t'[attr.aria-disabled]': 'effectiveReadonly() ? \"true\" : null',\n\t\t'[class.ntl-editor-disabled]': 'effectiveReadonly()',\n\t},\n})\nexport class NotectlEditorComponent implements ControlValueAccessor {\n\tprivate readonly destroyRef: DestroyRef = inject(DestroyRef);\n\tprivate readonly defaultConfig: Partial<NotectlEditorConfig> | null = inject(\n\t\tNOTECTL_DEFAULT_CONFIG,\n\t\t{ optional: true },\n\t);\n\tprivate readonly contentFormat: ContentFormat =\n\t\tinject(NOTECTL_CONTENT_FORMAT, { optional: true }) ?? 'json';\n\n\treadonly plugins = input<readonly Plugin[] | undefined>(undefined);\n\treadonly toolbar = input<NotectlEditorConfig['toolbar']>(undefined);\n\treadonly features = input<Partial<TextFormattingConfig> | undefined>(undefined);\n\treadonly placeholder = input<string | undefined>(undefined);\n\treadonly readonlyMode = input<boolean | undefined>(undefined);\n\treadonly autofocus = input<boolean | undefined>(undefined);\n\treadonly maxHistoryDepth = input<number | undefined>(undefined);\n\treadonly theme = input<ThemePreset | Theme | undefined>(undefined);\n\treadonly paperSize = input<PaperSize | undefined>(undefined);\n\treadonly dir = input<'ltr' | 'rtl' | undefined>(undefined);\n\treadonly locale = input<Locale | undefined>(undefined);\n\treadonly styleNonce = input<string | undefined>(undefined);\n\n\treadonly content: ModelSignal<Document | undefined> = model<Document | undefined>(undefined);\n\n\treadonly stateChange = output<StateChangeEvent>();\n\treadonly selectionChange = output<SelectionChangeEvent>();\n\treadonly editorFocus = output<void>();\n\treadonly editorBlur = output<void>();\n\treadonly ready = output<void>();\n\n\treadonly editorState = signal<EditorState | null>(null);\n\treadonly isEmpty = computed<boolean>(() => {\n\t\tconst state: EditorState | null = this.editorState();\n\t\tif (!state) return true;\n\t\tconst doc: Document = state.doc;\n\t\tif (doc.children.length === 0) return true;\n\t\tif (doc.children.length > 1) return false;\n\t\tconst block = doc.children[0];\n\t\tif (!block) return true;\n\t\treturn block.type === 'paragraph' && block.children.length === 0;\n\t});\n\n\tprivate readonly resolvedPlugins = computed<readonly Plugin[]>(\n\t\t() => this.plugins() ?? this.defaultConfig?.plugins ?? [],\n\t);\n\tprivate readonly resolvedToolbar = computed<NotectlEditorConfig['toolbar']>(\n\t\t() => this.toolbar() ?? this.defaultConfig?.toolbar,\n\t);\n\tprivate readonly resolvedFeatures = computed<Partial<TextFormattingConfig> | undefined>(\n\t\t() => this.features() ?? this.defaultConfig?.features,\n\t);\n\tprivate readonly resolvedPlaceholder = computed<string>(\n\t\t() => this.placeholder() ?? this.defaultConfig?.placeholder ?? 'Start typing...',\n\t);\n\tprivate readonly resolvedReadonlyMode = computed<boolean>(\n\t\t() => this.readonlyMode() ?? this.defaultConfig?.readonly ?? false,\n\t);\n\tprivate readonly resolvedAutofocus = computed<boolean>(\n\t\t() => this.autofocus() ?? this.defaultConfig?.autofocus ?? false,\n\t);\n\tprivate readonly resolvedMaxHistoryDepth = computed<number | undefined>(\n\t\t() => this.maxHistoryDepth() ?? this.defaultConfig?.maxHistoryDepth,\n\t);\n\tprivate readonly resolvedTheme = computed<ThemePreset | Theme>(\n\t\t() => this.theme() ?? this.defaultConfig?.theme ?? ThemePreset.Light,\n\t);\n\tprivate readonly resolvedPaperSize = computed<PaperSize | undefined>(\n\t\t() => this.paperSize() ?? this.defaultConfig?.paperSize,\n\t);\n\tprivate readonly resolvedDir = computed<'ltr' | 'rtl' | undefined>(\n\t\t() => this.dir() ?? this.defaultConfig?.dir,\n\t);\n\tprivate readonly resolvedLocale = computed<Locale | undefined>(\n\t\t() => this.locale() ?? this.defaultConfig?.locale,\n\t);\n\tprivate readonly resolvedStyleNonce = computed<string | undefined>(\n\t\t() => this.styleNonce() ?? this.defaultConfig?.styleNonce,\n\t);\n\n\treadonly effectiveReadonly = computed<boolean>(\n\t\t() => this.disabledByForms() || this.resolvedReadonlyMode(),\n\t);\n\n\tprivate readonly hostRef = viewChild.required<ElementRef<HTMLDivElement>>('host');\n\tprivate readonly initialized = signal(false);\n\tprivate readonly disabledByForms = signal(false);\n\n\tprivate readonly valueController = new EditorValueController({\n\t\temitControlValue: (value: NotectlValue) => this.onChange(value),\n\t\tgetEditor: () => this.editorRef,\n\t\tgetFormat: () => this.contentFormat,\n\t\tupdateContent: (doc: Document) => this.content.set(doc),\n\t\twhenReady: () => this.readyPromise,\n\t});\n\n\tprivate editorRef: NotectlEditor | null = null;\n\tprivate readyResolve: (() => void) | null = null;\n\tprivate readyPromise!: Promise<void>;\n\tprivate lastInitConfig: InitConfigSnapshot | null = null;\n\tprivate queuedInitConfig: InitConfigSnapshot | null = null;\n\tprivate reinitializePromise: Promise<void> | null = null;\n\tprivate pendingInitialDocument: Document | undefined;\n\tprivate onChange: (value: NotectlValue) => void = () => {};\n\tprivate onTouched: () => void = () => {};\n\n\tconstructor() {\n\t\tthis.resetReadyPromise();\n\n\t\tafterNextRender(() => {\n\t\t\tthis.initEditor(this.captureInitConfig());\n\t\t});\n\n\t\teffect(() => {\n\t\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\t\tif (!this.initialized() || !editor) return;\n\n\t\t\teditor.setTheme(this.resolvedTheme());\n\t\t\teditor.configure({\n\t\t\t\tdir: this.resolvedDir(),\n\t\t\t\tpaperSize: this.resolvedPaperSize(),\n\t\t\t\tplaceholder: this.resolvedPlaceholder(),\n\t\t\t\treadonly: this.effectiveReadonly(),\n\t\t\t});\n\t\t});\n\n\t\teffect(() => {\n\t\t\tconst doc: Document | undefined = this.content();\n\t\t\tif (!doc) return;\n\t\t\tthis.valueController.syncExternalContent(doc);\n\t\t});\n\n\t\teffect(() => {\n\t\t\tthis.scheduleReinitialization(this.captureInitConfig());\n\t\t});\n\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tvoid this.destroyEditor();\n\t\t});\n\t}\n\n\tgetJSON(): Document {\n\t\treturn this.requireEditor().getJSON();\n\t}\n\n\tsetJSON(doc: Document): void {\n\t\tvoid this.valueController.setDocument(doc);\n\t}\n\n\tasync getContentHTML(): Promise<string> {\n\t\treturn this.requireEditor().getContentHTML();\n\t}\n\n\tasync setContentHTML(html: string): Promise<void> {\n\t\tawait this.valueController.setSerializedValue(html);\n\t}\n\n\tgetText(): string {\n\t\treturn this.requireEditor().getText();\n\t}\n\n\tget commands(): NotectlEditor['commands'] {\n\t\treturn this.requireEditor().commands;\n\t}\n\n\tcan(): ReturnType<NotectlEditor['can']> {\n\t\treturn this.requireEditor().can();\n\t}\n\n\texecuteCommand(name: string): boolean {\n\t\treturn this.editorRef?.executeCommand(name) ?? false;\n\t}\n\n\tconfigurePlugin(pluginId: string, config: PluginConfig): void {\n\t\tthis.editorRef?.configurePlugin(pluginId, config);\n\t}\n\n\tdispatch(tr: Transaction): void {\n\t\tthis.editorRef?.dispatch(tr);\n\t}\n\n\tgetState(): EditorState {\n\t\treturn this.requireEditor().getState();\n\t}\n\n\tsetTheme(theme: ThemePreset | Theme): void {\n\t\tthis.editorRef?.setTheme(theme);\n\t}\n\n\tgetTheme(): ThemePreset | Theme {\n\t\treturn this.editorRef?.getTheme() ?? this.resolvedTheme();\n\t}\n\n\tsetReadonly(readonlyState: boolean): void {\n\t\tthis.editorRef?.configure({ readonly: readonlyState });\n\t}\n\n\twhenReady(): Promise<void> {\n\t\treturn this.readyPromise;\n\t}\n\n\tfocus(options?: FocusOptions): void {\n\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\tif (!editor) return;\n\n\t\tconst focusTarget =\n\t\t\teditor.shadowRoot?.querySelector<HTMLElement>('[contenteditable]') ?? editor;\n\t\tfocusTarget.focus(options);\n\t}\n\n\twriteValue(value: NotectlValue): void {\n\t\tthis.valueController.writeControlValue(value);\n\t}\n\n\tregisterOnChange(fn: (value: NotectlValue) => void): void {\n\t\tthis.onChange = fn;\n\t}\n\n\tregisterOnTouched(fn: () => void): void {\n\t\tthis.onTouched = fn;\n\t}\n\n\tsetDisabledState(isDisabled: boolean): void {\n\t\tthis.disabledByForms.set(isDisabled);\n\t}\n\n\tprivate resetReadyPromise(): void {\n\t\tthis.readyPromise = new Promise<void>((resolve) => {\n\t\t\tthis.readyResolve = resolve;\n\t\t});\n\t}\n\n\tprivate initEditor(snapshot: InitConfigSnapshot): void {\n\t\tconst hostElement: HTMLDivElement = this.hostRef().nativeElement;\n\t\tconst editor = new NotectlEditor();\n\t\tthis.editorRef = editor;\n\t\tthis.lastInitConfig = snapshot;\n\t\tthis.valueController.reset();\n\n\t\teditor.on('stateChange', (event: StateChangeEvent) => {\n\t\t\tthis.editorState.set(event.newState);\n\t\t\tthis.valueController.handleEditorStateChange(event.newState.doc);\n\t\t\tthis.stateChange.emit(event);\n\t\t});\n\n\t\teditor.on('selectionChange', (event: { selection: EditorSelection }) => {\n\t\t\tthis.selectionChange.emit(event);\n\t\t});\n\n\t\teditor.on('focus', () => {\n\t\t\tthis.editorFocus.emit();\n\t\t});\n\n\t\teditor.on('blur', () => {\n\t\t\tthis.onTouched();\n\t\t\tthis.editorBlur.emit();\n\t\t});\n\n\t\teditor.on('ready', () => {\n\t\t\tthis.initialized.set(true);\n\t\t\tthis.editorState.set(editor.getState());\n\t\t\tvoid this.applyInitialContent(editor).finally(() => {\n\t\t\t\tthis.readyResolve?.();\n\t\t\t\tthis.ready.emit();\n\t\t\t});\n\t\t});\n\n\t\teditor.init(this.buildConfig());\n\t\thostElement.appendChild(editor);\n\t}\n\n\tprivate async applyInitialContent(editor: NotectlEditor): Promise<void> {\n\t\tconst currentContent: Document | undefined = this.content();\n\t\tconst initialContent = currentContent ?? this.pendingInitialDocument;\n\t\tthis.pendingInitialDocument = undefined;\n\n\t\tif (!initialContent) return;\n\t\tawait this.valueController.applyInitialDocument(editor, initialContent);\n\t}\n\n\tprivate buildConfig(): NotectlEditorConfig {\n\t\tconst config: NotectlEditorConfig = {\n\t\t\t...this.defaultConfig,\n\t\t\tautofocus: this.resolvedAutofocus(),\n\t\t\tdir: this.resolvedDir(),\n\t\t\tlocale: this.resolvedLocale(),\n\t\t\tmaxHistoryDepth: this.resolvedMaxHistoryDepth(),\n\t\t\tpaperSize: this.resolvedPaperSize(),\n\t\t\tplaceholder: this.resolvedPlaceholder(),\n\t\t\tplugins: this.resolvedPlugins(),\n\t\t\treadonly: this.effectiveReadonly(),\n\t\t\tstyleNonce: this.resolvedStyleNonce(),\n\t\t\ttheme: this.resolvedTheme(),\n\t\t};\n\n\t\tconst toolbar = this.resolvedToolbar();\n\t\tif (toolbar !== undefined) {\n\t\t\tconfig.toolbar = toolbar;\n\t\t}\n\n\t\tconst features = this.resolvedFeatures();\n\t\tif (features !== undefined) {\n\t\t\tconfig.features = features;\n\t\t}\n\n\t\treturn config;\n\t}\n\n\tprivate captureInitConfig(): InitConfigSnapshot {\n\t\treturn {\n\t\t\tautofocus: this.resolvedAutofocus(),\n\t\t\tfeatures: this.resolvedFeatures(),\n\t\t\tlocale: this.resolvedLocale(),\n\t\t\tmaxHistoryDepth: this.resolvedMaxHistoryDepth(),\n\t\t\tplugins: this.resolvedPlugins(),\n\t\t\tstyleNonce: this.resolvedStyleNonce(),\n\t\t\ttoolbar: this.resolvedToolbar(),\n\t\t};\n\t}\n\n\tprivate scheduleReinitialization(snapshot: InitConfigSnapshot): void {\n\t\tif (!this.initialized() || !this.lastInitConfig) return;\n\t\tif (initConfigEquals(snapshot, this.lastInitConfig)) return;\n\n\t\tthis.queuedInitConfig = snapshot;\n\t\tif (this.reinitializePromise) return;\n\n\t\tthis.reinitializePromise = (async () => {\n\t\t\twhile (this.queuedInitConfig) {\n\t\t\t\tconst nextSnapshot = this.queuedInitConfig;\n\t\t\t\tthis.queuedInitConfig = null;\n\t\t\t\tthis.pendingInitialDocument = this.editorRef?.getJSON();\n\t\t\t\tthis.resetReadyPromise();\n\t\t\t\tthis.initialized.set(false);\n\t\t\t\tawait this.destroyEditor();\n\t\t\t\tthis.initEditor(nextSnapshot);\n\t\t\t\tawait this.readyPromise;\n\t\t\t}\n\t\t})().finally(() => {\n\t\t\tthis.reinitializePromise = null;\n\t\t});\n\t}\n\n\tprivate async destroyEditor(): Promise<void> {\n\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\tif (!editor) {\n\t\t\tthis.initialized.set(false);\n\t\t\tthis.editorState.set(null);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.editorRef = null;\n\t\teditor.remove();\n\t\tawait editor.destroy();\n\t\tthis.initialized.set(false);\n\t\tthis.editorState.set(null);\n\t}\n\n\tprivate requireEditor(): NotectlEditor {\n\t\tconst editor: NotectlEditor | null = this.editorRef;\n\t\tif (!editor || !this.initialized()) {\n\t\t\tthrow new Error('NotectlEditor is not initialized. Await whenReady() first.');\n\t\t}\n\t\treturn editor;\n\t}\n}\n","import { Directive } from '@angular/core';\n\n/**\n * @deprecated Angular Forms support is built into `NotectlEditorComponent`.\n *\n * This directive remains as a compatibility shim so existing imports do not break,\n * but it no longer participates in value accessor registration.\n */\n@Directive({\n\tselector: 'ntl-editor[formControl], ntl-editor[formControlName], ntl-editor[ngModel]',\n\tstandalone: true,\n})\nexport class NotectlValueAccessorDirective {}\n","import { DestroyRef, Injectable, computed, inject, signal } from '@angular/core';\nimport type { EditorState, StateChangeEvent, Transaction } from '@notectl/core';\nimport { type Observable, Subject } from 'rxjs';\n\nimport type { NotectlEditorComponent } from './notectl-editor.component';\n\n/**\n * Optional injectable service for programmatic editor access via DI.\n *\n * Useful when a toolbar or other component needs to interact with\n * the editor without a direct template reference.\n *\n * The service must be provided at a shared injector level and\n * connected to the editor component via `register()`.\n *\n * @example\n * ```typescript\n * @Component({\n * providers: [NotectlEditorService],\n * template: `\n * <app-toolbar />\n * <ntl-editor #editor [plugins]=\"plugins\" />\n * `,\n * })\n * export class EditorPage {\n * private readonly editor = viewChild.required<NotectlEditorComponent>('editor');\n * private readonly service = inject(NotectlEditorService);\n *\n * constructor() {\n * afterNextRender(() => {\n * this.service.register(this.editor());\n * });\n * }\n * }\n * ```\n */\n@Injectable()\nexport class NotectlEditorService {\n\tprivate readonly destroyRef: DestroyRef = inject(DestroyRef);\n\tprivate readonly editorRef = signal<NotectlEditorComponent | null>(null);\n\tprivate readonly stateChangeSubject = new Subject<StateChangeEvent>();\n\n\t/** Whether an editor is currently registered with this service. */\n\treadonly hasEditor = computed<boolean>(() => this.editorRef() !== null);\n\n\t/** Observable stream of state change events from the editor. */\n\treadonly stateChanges$: Observable<StateChangeEvent> = this.stateChangeSubject.asObservable();\n\n\tprivate stateChangeSub: { unsubscribe(): void } | null = null;\n\n\tconstructor() {\n\t\tthis.destroyRef.onDestroy(() => {\n\t\t\tthis.unregister();\n\t\t\tthis.stateChangeSubject.complete();\n\t\t});\n\t}\n\n\t/** Registers an editor component with this service. */\n\tregister(editor: NotectlEditorComponent): void {\n\t\tthis.unregister();\n\t\tthis.editorRef.set(editor);\n\n\t\tthis.stateChangeSub = editor.stateChange.subscribe((event: StateChangeEvent) => {\n\t\t\tthis.stateChangeSubject.next(event);\n\t\t});\n\t}\n\n\t/** Unregisters the current editor from this service. */\n\tunregister(): void {\n\t\tthis.stateChangeSub?.unsubscribe();\n\t\tthis.stateChangeSub = null;\n\t\tthis.editorRef.set(null);\n\t}\n\n\t/** Executes a named command on the registered editor. */\n\texecuteCommand(name: string): boolean {\n\t\tconst editor: NotectlEditorComponent | null = this.editorRef();\n\t\tif (!editor) return false;\n\t\treturn editor.executeCommand(name);\n\t}\n\n\t/** Returns the current editor state, or `null` if no editor is registered. */\n\tgetState(): EditorState | null {\n\t\tconst editor: NotectlEditorComponent | null = this.editorRef();\n\t\tif (!editor) return null;\n\t\ttry {\n\t\t\treturn editor.getState();\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/** Dispatches a transaction on the registered editor. */\n\tdispatch(tr: Transaction): void {\n\t\tthis.editorRef()?.dispatch(tr);\n\t}\n}\n","/**\n * @notectl/angular — Angular integration for the notectl rich text editor.\n * @packageDocumentation\n */\n\n// --- Angular Bindings ---\nexport { NotectlEditorComponent } from './lib/notectl-editor.component';\nexport { NotectlValueAccessorDirective } from './lib/value-accessor.directive';\nexport { NotectlEditorService } from './lib/notectl-editor.service';\n\n// --- Provider Function ---\nexport {\n\tprovideNotectl,\n\ttype NotectlProviderOptions,\n} from './lib/tokens';\n\n// --- Injection Tokens ---\nexport {\n\tNOTECTL_DEFAULT_CONFIG,\n\tNOTECTL_CONTENT_FORMAT,\n\ttype ContentFormat,\n} from './lib/tokens';\n\n// --- Angular-specific Types ---\nexport type { NotectlValue, SelectionChangeEvent } from './lib/types';\n\n// --- Re-exports from @notectl/core (convenience) ---\n\n// Model types\nexport type {\n\tDocument,\n\tBlockNode,\n\tTextNode,\n\tInlineNode,\n\tMark,\n\tBlockAttrs,\n} from '@notectl/core';\n\n// Selection types\nexport type { EditorSelection, Position, Selection } from '@notectl/core';\n\n// State types\nexport type {\n\tEditorState,\n\tTransaction,\n\tTransactionMetadata,\n\tStateChangeEvent,\n} from '@notectl/core';\n\n// Plugin types\nexport type { Plugin, PluginConfig, PluginContext } from '@notectl/core';\n\n// Theme types\nexport type { Theme, PartialTheme, ThemePrimitives } from '@notectl/core';\nexport { ThemePreset, LIGHT_THEME, DARK_THEME, createTheme } from '@notectl/core';\n\n// Editor config\nexport type { NotectlEditorConfig } from '@notectl/core';\n\n// Plugin config types (from sub-path exports)\nexport type { TextFormattingConfig } from '@notectl/core/plugins/text-formatting';\nexport type { FontDefinition } from '@notectl/core/plugins/font';\n\n// Starter fonts\n/** @deprecated Import from '@notectl/core/fonts' instead. */\nexport { STARTER_FONTS } from '@notectl/core/fonts';\n\n// Plugins (tree-shakable re-exports from sub-paths)\nexport { TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';\nexport { HeadingPlugin } from '@notectl/core/plugins/heading';\nexport { ListPlugin } from '@notectl/core/plugins/list';\nexport { LinkPlugin } from '@notectl/core/plugins/link';\nexport { BlockquotePlugin } from '@notectl/core/plugins/blockquote';\nexport { CodeBlockPlugin } from '@notectl/core/plugins/code-block';\nexport { TablePlugin } from '@notectl/core/plugins/table';\nexport { ImagePlugin } from '@notectl/core/plugins/image';\nexport { HorizontalRulePlugin } from '@notectl/core/plugins/horizontal-rule';\nexport { HardBreakPlugin } from '@notectl/core/plugins/hard-break';\nexport { StrikethroughPlugin } from '@notectl/core/plugins/strikethrough';\nexport { HighlightPlugin } from '@notectl/core/plugins/highlight';\nexport { TextColorPlugin } from '@notectl/core/plugins/text-color';\nexport { FontPlugin } from '@notectl/core/plugins/font';\nexport { FontSizePlugin } from '@notectl/core/plugins/font-size';\nexport { AlignmentPlugin } from '@notectl/core/plugins/alignment';\nexport { TextDirectionPlugin } from '@notectl/core/plugins/text-direction';\nexport { SuperSubPlugin } from '@notectl/core/plugins/super-sub';\nexport { ToolbarPlugin } from '@notectl/core/plugins/toolbar';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import * as _notectl_core from '@notectl/core';
|
|
2
|
+
import { EditorSelection, Document, Plugin, ThemePreset, Theme, PaperSize, Locale, StateChangeEvent, EditorState, NotectlEditor, PluginConfig, Transaction, NotectlEditorConfig } from '@notectl/core';
|
|
3
|
+
export { BlockAttrs, BlockNode, DARK_THEME, Document, EditorSelection, EditorState, InlineNode, LIGHT_THEME, Mark, NotectlEditorConfig, PartialTheme, Plugin, PluginConfig, PluginContext, Position, Selection, StateChangeEvent, TextNode, Theme, ThemePreset, ThemePrimitives, Transaction, TransactionMetadata, createTheme } from '@notectl/core';
|
|
1
4
|
import * as _angular_core from '@angular/core';
|
|
2
5
|
import { ModelSignal, InjectionToken, EnvironmentProviders } from '@angular/core';
|
|
3
|
-
import {
|
|
4
|
-
export { BlockAttrs, BlockNode, DARK_THEME, Document, EditorSelection, EditorState, InlineNode, LIGHT_THEME, Mark, NotectlEditorConfig, PartialTheme, Plugin, PluginConfig, PluginContext, Position, Selection, StateChangeEvent, TextNode, Theme, ThemePreset, ThemePrimitives, Transaction, TransactionMetadata, createTheme } from '@notectl/core';
|
|
6
|
+
import { ControlValueAccessor } from '@angular/forms';
|
|
5
7
|
import { TextFormattingConfig } from '@notectl/core/plugins/text-formatting';
|
|
6
8
|
export { TextFormattingConfig, TextFormattingPlugin } from '@notectl/core/plugins/text-formatting';
|
|
7
|
-
import { ControlValueAccessor } from '@angular/forms';
|
|
8
9
|
import { Observable } from 'rxjs';
|
|
9
10
|
export { FontDefinition, FontPlugin } from '@notectl/core/plugins/font';
|
|
10
11
|
export { STARTER_FONTS } from '@notectl/core/fonts';
|
|
@@ -30,48 +31,25 @@ export { ToolbarPlugin } from '@notectl/core/plugins/toolbar';
|
|
|
30
31
|
interface SelectionChangeEvent {
|
|
31
32
|
readonly selection: EditorSelection;
|
|
32
33
|
}
|
|
34
|
+
/** Angular-facing value type used by forms and two-way binding. */
|
|
35
|
+
type NotectlValue = Document | string | null;
|
|
33
36
|
|
|
34
|
-
|
|
35
|
-
* Angular standalone component wrapping the `<ntl-editor>` Web Component.
|
|
36
|
-
*
|
|
37
|
-
* Uses `afterNextRender()` for SSR-safe initialization and `effect()` for
|
|
38
|
-
* reactive input tracking — no lifecycle interfaces needed.
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* ```html
|
|
42
|
-
* <!-- Basic usage -->
|
|
43
|
-
* <ntl-editor [toolbar]="toolbar" [plugins]="plugins" />
|
|
44
|
-
*
|
|
45
|
-
* <!-- Two-way content binding -->
|
|
46
|
-
* <ntl-editor [(content)]="myDocument" [toolbar]="toolbar" />
|
|
47
|
-
*
|
|
48
|
-
* <!-- Reactive forms -->
|
|
49
|
-
* <ntl-editor [formControl]="editorControl" [toolbar]="toolbar" />
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
declare class NotectlEditorComponent {
|
|
37
|
+
declare class NotectlEditorComponent implements ControlValueAccessor {
|
|
53
38
|
private readonly destroyRef;
|
|
54
39
|
private readonly defaultConfig;
|
|
55
|
-
readonly
|
|
56
|
-
readonly
|
|
40
|
+
private readonly contentFormat;
|
|
41
|
+
readonly plugins: _angular_core.InputSignal<readonly Plugin<Record<string, unknown>>[]>;
|
|
42
|
+
readonly toolbar: _angular_core.InputSignal<readonly (readonly Plugin<Record<string, unknown>>[])[] | _notectl_core.ToolbarConfig>;
|
|
57
43
|
readonly features: _angular_core.InputSignal<Partial<TextFormattingConfig>>;
|
|
58
44
|
readonly placeholder: _angular_core.InputSignal<string>;
|
|
59
45
|
readonly readonlyMode: _angular_core.InputSignal<boolean>;
|
|
60
46
|
readonly autofocus: _angular_core.InputSignal<boolean>;
|
|
61
47
|
readonly maxHistoryDepth: _angular_core.InputSignal<number>;
|
|
62
48
|
readonly theme: _angular_core.InputSignal<ThemePreset | Theme>;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
* its own state. Once the editor emits changes, the model updates to the
|
|
68
|
-
* current `Document`.
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* ```html
|
|
72
|
-
* <ntl-editor [(content)]="myDocument" />
|
|
73
|
-
* ```
|
|
74
|
-
*/
|
|
49
|
+
readonly paperSize: _angular_core.InputSignal<PaperSize>;
|
|
50
|
+
readonly dir: _angular_core.InputSignal<"ltr" | "rtl">;
|
|
51
|
+
readonly locale: _angular_core.InputSignal<Locale>;
|
|
52
|
+
readonly styleNonce: _angular_core.InputSignal<string>;
|
|
75
53
|
readonly content: ModelSignal<Document | undefined>;
|
|
76
54
|
readonly stateChange: _angular_core.OutputEmitterRef<StateChangeEvent>;
|
|
77
55
|
readonly selectionChange: _angular_core.OutputEmitterRef<SelectionChangeEvent>;
|
|
@@ -80,85 +58,72 @@ declare class NotectlEditorComponent {
|
|
|
80
58
|
readonly ready: _angular_core.OutputEmitterRef<void>;
|
|
81
59
|
readonly editorState: _angular_core.WritableSignal<EditorState>;
|
|
82
60
|
readonly isEmpty: _angular_core.Signal<boolean>;
|
|
61
|
+
private readonly resolvedPlugins;
|
|
62
|
+
private readonly resolvedToolbar;
|
|
63
|
+
private readonly resolvedFeatures;
|
|
64
|
+
private readonly resolvedPlaceholder;
|
|
65
|
+
private readonly resolvedReadonlyMode;
|
|
66
|
+
private readonly resolvedAutofocus;
|
|
67
|
+
private readonly resolvedMaxHistoryDepth;
|
|
68
|
+
private readonly resolvedTheme;
|
|
69
|
+
private readonly resolvedPaperSize;
|
|
70
|
+
private readonly resolvedDir;
|
|
71
|
+
private readonly resolvedLocale;
|
|
72
|
+
private readonly resolvedStyleNonce;
|
|
73
|
+
readonly effectiveReadonly: _angular_core.Signal<boolean>;
|
|
83
74
|
private readonly hostRef;
|
|
75
|
+
private readonly initialized;
|
|
76
|
+
private readonly disabledByForms;
|
|
77
|
+
private readonly valueController;
|
|
84
78
|
private editorRef;
|
|
85
79
|
private readyResolve;
|
|
86
|
-
private
|
|
87
|
-
private
|
|
88
|
-
|
|
89
|
-
private
|
|
80
|
+
private readyPromise;
|
|
81
|
+
private lastInitConfig;
|
|
82
|
+
private queuedInitConfig;
|
|
83
|
+
private reinitializePromise;
|
|
84
|
+
private pendingInitialDocument;
|
|
85
|
+
private onChange;
|
|
86
|
+
private onTouched;
|
|
90
87
|
constructor();
|
|
91
|
-
/** Returns the document as JSON. */
|
|
92
88
|
getJSON(): Document;
|
|
93
|
-
/** Sets the document from JSON. */
|
|
94
89
|
setJSON(doc: Document): void;
|
|
95
|
-
/** Returns sanitized HTML representation of the document. */
|
|
96
90
|
getContentHTML(): Promise<string>;
|
|
97
|
-
/** Sets content from HTML (sanitized). */
|
|
98
91
|
setContentHTML(html: string): Promise<void>;
|
|
99
|
-
/** Returns plain text content. */
|
|
100
92
|
getText(): string;
|
|
101
|
-
/** Proxy to the Web Component's `commands` object. */
|
|
102
93
|
get commands(): NotectlEditor['commands'];
|
|
103
|
-
/** Proxy to the Web Component's `can()` capability checker. */
|
|
104
94
|
can(): ReturnType<NotectlEditor['can']>;
|
|
105
|
-
/** Executes a named command registered by a plugin. */
|
|
106
95
|
executeCommand(name: string): boolean;
|
|
107
|
-
/** Configures a plugin at runtime. */
|
|
108
96
|
configurePlugin(pluginId: string, config: PluginConfig): void;
|
|
109
|
-
/** Dispatches a transaction. */
|
|
110
97
|
dispatch(tr: Transaction): void;
|
|
111
|
-
/** Returns the current editor state. */
|
|
112
98
|
getState(): EditorState;
|
|
113
|
-
/** Changes the theme at runtime. */
|
|
114
99
|
setTheme(theme: ThemePreset | Theme): void;
|
|
115
|
-
/** Returns the current theme setting. */
|
|
116
100
|
getTheme(): ThemePreset | Theme;
|
|
117
|
-
/** Sets the readonly state programmatically (used by ControlValueAccessor). */
|
|
118
101
|
setReadonly(readonlyState: boolean): void;
|
|
119
|
-
/** Resolves when the editor is ready. */
|
|
120
102
|
whenReady(): Promise<void>;
|
|
103
|
+
focus(options?: FocusOptions): void;
|
|
104
|
+
writeValue(value: NotectlValue): void;
|
|
105
|
+
registerOnChange(fn: (value: NotectlValue) => void): void;
|
|
106
|
+
registerOnTouched(fn: () => void): void;
|
|
107
|
+
setDisabledState(isDisabled: boolean): void;
|
|
108
|
+
private resetReadyPromise;
|
|
121
109
|
private initEditor;
|
|
110
|
+
private applyInitialContent;
|
|
122
111
|
private buildConfig;
|
|
123
|
-
|
|
124
|
-
private
|
|
112
|
+
private captureInitConfig;
|
|
113
|
+
private scheduleReinitialization;
|
|
125
114
|
private destroyEditor;
|
|
126
115
|
private requireEditor;
|
|
127
116
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<NotectlEditorComponent, never>;
|
|
128
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<NotectlEditorComponent, "ntl-editor", never, { "plugins": { "alias": "plugins"; "required": false; "isSignal": true; }; "toolbar": { "alias": "toolbar"; "required": false; "isSignal": true; }; "features": { "alias": "features"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "readonlyMode": { "alias": "readonlyMode"; "required": false; "isSignal": true; }; "autofocus": { "alias": "autofocus"; "required": false; "isSignal": true; }; "maxHistoryDepth": { "alias": "maxHistoryDepth"; "required": false; "isSignal": true; }; "theme": { "alias": "theme"; "required": false; "isSignal": true; }; "content": { "alias": "content"; "required": false; "isSignal": true; }; }, { "content": "contentChange"; "stateChange": "stateChange"; "selectionChange": "selectionChange"; "editorFocus": "editorFocus"; "editorBlur": "editorBlur"; "ready": "ready"; }, never, never, true, never>;
|
|
117
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<NotectlEditorComponent, "ntl-editor", never, { "plugins": { "alias": "plugins"; "required": false; "isSignal": true; }; "toolbar": { "alias": "toolbar"; "required": false; "isSignal": true; }; "features": { "alias": "features"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "readonlyMode": { "alias": "readonlyMode"; "required": false; "isSignal": true; }; "autofocus": { "alias": "autofocus"; "required": false; "isSignal": true; }; "maxHistoryDepth": { "alias": "maxHistoryDepth"; "required": false; "isSignal": true; }; "theme": { "alias": "theme"; "required": false; "isSignal": true; }; "paperSize": { "alias": "paperSize"; "required": false; "isSignal": true; }; "dir": { "alias": "dir"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "styleNonce": { "alias": "styleNonce"; "required": false; "isSignal": true; }; "content": { "alias": "content"; "required": false; "isSignal": true; }; }, { "content": "contentChange"; "stateChange": "stateChange"; "selectionChange": "selectionChange"; "editorFocus": "editorFocus"; "editorBlur": "editorBlur"; "ready": "ready"; }, never, never, true, never>;
|
|
129
118
|
}
|
|
130
119
|
|
|
131
|
-
type OnChangeFn = (value: Document | string | null) => void;
|
|
132
|
-
type OnTouchedFn = () => void;
|
|
133
120
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* Supports Reactive Forms (`formControl`, `formControlName`) and
|
|
137
|
-
* template-driven forms (`ngModel`).
|
|
121
|
+
* @deprecated Angular Forms support is built into `NotectlEditorComponent`.
|
|
138
122
|
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
* - `'json'` (default) — form value is a `Document` object
|
|
142
|
-
* - `'html'` — form value is a sanitized HTML string
|
|
143
|
-
* - `'text'` — form value is a plain text string
|
|
123
|
+
* This directive remains as a compatibility shim so existing imports do not break,
|
|
124
|
+
* but it no longer participates in value accessor registration.
|
|
144
125
|
*/
|
|
145
|
-
declare class NotectlValueAccessorDirective
|
|
146
|
-
private readonly editor;
|
|
147
|
-
private readonly destroyRef;
|
|
148
|
-
private readonly format;
|
|
149
|
-
private onChange;
|
|
150
|
-
private onTouched;
|
|
151
|
-
private pendingValue;
|
|
152
|
-
private suppressEmit;
|
|
153
|
-
private readonly stateChangeSub;
|
|
154
|
-
private readonly blurSub;
|
|
155
|
-
constructor();
|
|
156
|
-
writeValue(value: Document | string | null): void;
|
|
157
|
-
registerOnChange(fn: OnChangeFn): void;
|
|
158
|
-
registerOnTouched(fn: OnTouchedFn): void;
|
|
159
|
-
setDisabledState(isDisabled: boolean): void;
|
|
160
|
-
private writeValueToEditor;
|
|
161
|
-
private readValue;
|
|
126
|
+
declare class NotectlValueAccessorDirective {
|
|
162
127
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<NotectlValueAccessorDirective, never>;
|
|
163
128
|
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NotectlValueAccessorDirective, "ntl-editor[formControl], ntl-editor[formControlName], ntl-editor[ngModel]", never, {}, {}, never, never, true, never>;
|
|
164
129
|
}
|
|
@@ -258,4 +223,4 @@ interface NotectlProviderOptions {
|
|
|
258
223
|
declare function provideNotectl(options?: NotectlProviderOptions): EnvironmentProviders;
|
|
259
224
|
|
|
260
225
|
export { NOTECTL_CONTENT_FORMAT, NOTECTL_DEFAULT_CONFIG, NotectlEditorComponent, NotectlEditorService, NotectlValueAccessorDirective, provideNotectl };
|
|
261
|
-
export type { ContentFormat, NotectlProviderOptions, SelectionChangeEvent };
|
|
226
|
+
export type { ContentFormat, NotectlProviderOptions, NotectlValue, SelectionChangeEvent };
|
package/package.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notectl/angular",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Angular integration for the notectl rich text editor. Provides a standalone component, reactive forms support, and DI service.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"main": "./dist/fesm2022/notectl-angular.mjs",
|
|
7
8
|
"module": "./dist/fesm2022/notectl-angular.mjs",
|
|
9
|
+
"types": "./dist/types/notectl-angular.d.ts",
|
|
8
10
|
"typings": "./dist/types/notectl-angular.d.ts",
|
|
9
11
|
"sideEffects": false,
|
|
10
12
|
"files": ["dist", "README.md", "LICENSE"],
|
|
@@ -41,7 +43,7 @@
|
|
|
41
43
|
"peerDependencies": {
|
|
42
44
|
"@angular/core": ">=21.0.0",
|
|
43
45
|
"@angular/forms": ">=21.0.0",
|
|
44
|
-
"@notectl/core": "^2.0.
|
|
46
|
+
"@notectl/core": "^2.0.2",
|
|
45
47
|
"rxjs": ">=7.0.0"
|
|
46
48
|
},
|
|
47
49
|
"devDependencies": {
|