@notectl/angular 2.0.1 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +258 -121
- package/dist/fesm2022/notectl-angular.mjs +342 -271
- package/dist/fesm2022/notectl-angular.mjs.map +1 -1
- package/dist/types/notectl-angular.d.ts +52 -87
- package/package.json +12 -10
|
@@ -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,59 +201,38 @@ 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" }] : /* istanbul ignore next */ []));
|
|
218
|
+
toolbar = input(undefined, ...(ngDevMode ? [{ debugName: "toolbar" }] : /* istanbul ignore next */ []));
|
|
219
|
+
features = input(undefined, ...(ngDevMode ? [{ debugName: "features" }] : /* istanbul ignore next */ []));
|
|
220
|
+
placeholder = input(undefined, ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
221
|
+
readonlyMode = input(undefined, ...(ngDevMode ? [{ debugName: "readonlyMode" }] : /* istanbul ignore next */ []));
|
|
222
|
+
autofocus = input(undefined, ...(ngDevMode ? [{ debugName: "autofocus" }] : /* istanbul ignore next */ []));
|
|
223
|
+
maxHistoryDepth = input(undefined, ...(ngDevMode ? [{ debugName: "maxHistoryDepth" }] : /* istanbul ignore next */ []));
|
|
224
|
+
theme = input(undefined, ...(ngDevMode ? [{ debugName: "theme" }] : /* istanbul ignore next */ []));
|
|
225
|
+
paperSize = input(undefined, ...(ngDevMode ? [{ debugName: "paperSize" }] : /* istanbul ignore next */ []));
|
|
226
|
+
dir = input(undefined, ...(ngDevMode ? [{ debugName: "dir" }] : /* istanbul ignore next */ []));
|
|
227
|
+
locale = input(undefined, ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
|
|
228
|
+
styleNonce = input(undefined, ...(ngDevMode ? [{ debugName: "styleNonce" }] : /* istanbul ignore next */ []));
|
|
229
|
+
content = model(undefined, ...(ngDevMode ? [{ debugName: "content" }] : /* istanbul ignore next */ []));
|
|
114
230
|
stateChange = output();
|
|
115
231
|
selectionChange = output();
|
|
116
232
|
editorFocus = output();
|
|
117
233
|
editorBlur = output();
|
|
118
234
|
ready = output();
|
|
119
|
-
|
|
120
|
-
editorState = signal(null, ...(ngDevMode ? [{ debugName: "editorState" }] : []));
|
|
235
|
+
editorState = signal(null, ...(ngDevMode ? [{ debugName: "editorState" }] : /* istanbul ignore next */ []));
|
|
121
236
|
isEmpty = computed(() => {
|
|
122
237
|
const state = this.editorState();
|
|
123
238
|
if (!state)
|
|
@@ -131,126 +246,147 @@ class NotectlEditorComponent {
|
|
|
131
246
|
if (!block)
|
|
132
247
|
return true;
|
|
133
248
|
return block.type === 'paragraph' && block.children.length === 0;
|
|
134
|
-
}, ...(ngDevMode ? [{ debugName: "isEmpty" }] : []));
|
|
135
|
-
|
|
249
|
+
}, ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
|
|
250
|
+
resolvedPlugins = computed(() => this.plugins() ?? this.defaultConfig?.plugins ?? [], ...(ngDevMode ? [{ debugName: "resolvedPlugins" }] : /* istanbul ignore next */ []));
|
|
251
|
+
resolvedToolbar = computed(() => this.toolbar() ?? this.defaultConfig?.toolbar, ...(ngDevMode ? [{ debugName: "resolvedToolbar" }] : /* istanbul ignore next */ []));
|
|
252
|
+
resolvedFeatures = computed(() => this.features() ?? this.defaultConfig?.features, ...(ngDevMode ? [{ debugName: "resolvedFeatures" }] : /* istanbul ignore next */ []));
|
|
253
|
+
resolvedPlaceholder = computed(() => this.placeholder() ?? this.defaultConfig?.placeholder ?? 'Start typing...', ...(ngDevMode ? [{ debugName: "resolvedPlaceholder" }] : /* istanbul ignore next */ []));
|
|
254
|
+
resolvedReadonlyMode = computed(() => this.readonlyMode() ?? this.defaultConfig?.readonly ?? false, ...(ngDevMode ? [{ debugName: "resolvedReadonlyMode" }] : /* istanbul ignore next */ []));
|
|
255
|
+
resolvedAutofocus = computed(() => this.autofocus() ?? this.defaultConfig?.autofocus ?? false, ...(ngDevMode ? [{ debugName: "resolvedAutofocus" }] : /* istanbul ignore next */ []));
|
|
256
|
+
resolvedMaxHistoryDepth = computed(() => this.maxHistoryDepth() ?? this.defaultConfig?.maxHistoryDepth, ...(ngDevMode ? [{ debugName: "resolvedMaxHistoryDepth" }] : /* istanbul ignore next */ []));
|
|
257
|
+
resolvedTheme = computed(() => this.theme() ?? this.defaultConfig?.theme ?? ThemePreset.Light, ...(ngDevMode ? [{ debugName: "resolvedTheme" }] : /* istanbul ignore next */ []));
|
|
258
|
+
resolvedPaperSize = computed(() => this.paperSize() ?? this.defaultConfig?.paperSize, ...(ngDevMode ? [{ debugName: "resolvedPaperSize" }] : /* istanbul ignore next */ []));
|
|
259
|
+
resolvedDir = computed(() => this.dir() ?? this.defaultConfig?.dir, ...(ngDevMode ? [{ debugName: "resolvedDir" }] : /* istanbul ignore next */ []));
|
|
260
|
+
resolvedLocale = computed(() => this.locale() ?? this.defaultConfig?.locale, ...(ngDevMode ? [{ debugName: "resolvedLocale" }] : /* istanbul ignore next */ []));
|
|
261
|
+
resolvedStyleNonce = computed(() => this.styleNonce() ?? this.defaultConfig?.styleNonce, ...(ngDevMode ? [{ debugName: "resolvedStyleNonce" }] : /* istanbul ignore next */ []));
|
|
262
|
+
effectiveReadonly = computed(() => this.disabledByForms() || this.resolvedReadonlyMode(), ...(ngDevMode ? [{ debugName: "effectiveReadonly" }] : /* istanbul ignore next */ []));
|
|
136
263
|
hostRef = viewChild.required('host');
|
|
264
|
+
initialized = signal(false, ...(ngDevMode ? [{ debugName: "initialized" }] : /* istanbul ignore next */ []));
|
|
265
|
+
disabledByForms = signal(false, ...(ngDevMode ? [{ debugName: "disabledByForms" }] : /* istanbul ignore next */ []));
|
|
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
|
+
};
|
|
455
|
+
}
|
|
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
|
+
});
|
|
312
478
|
}
|
|
313
|
-
destroyEditor() {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
this.
|
|
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
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
328
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.
|
|
499
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NotectlEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
500
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.4", 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
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
508
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", 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
|
-
|
|
358
|
-
|
|
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
|
-
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]", providers: [
|
|
448
|
-
{
|
|
449
|
-
provide: NG_VALUE_ACCESSOR,
|
|
450
|
-
useExisting: forwardRef(() => NotectlValueAccessorDirective),
|
|
451
|
-
multi: true,
|
|
452
|
-
},
|
|
453
|
-
], ngImport: i0 });
|
|
529
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NotectlValueAccessorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
530
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.4", type: NotectlValueAccessorDirective, isStandalone: true, selector: "ntl-editor[formControl], ntl-editor[formControlName], ntl-editor[ngModel]", ngImport: i0 });
|
|
454
531
|
}
|
|
455
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
532
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", 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.
|
|
@@ -498,10 +569,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
498
569
|
*/
|
|
499
570
|
class NotectlEditorService {
|
|
500
571
|
destroyRef = inject(DestroyRef);
|
|
501
|
-
editorRef = signal(null, ...(ngDevMode ? [{ debugName: "editorRef" }] : []));
|
|
572
|
+
editorRef = signal(null, ...(ngDevMode ? [{ debugName: "editorRef" }] : /* istanbul ignore next */ []));
|
|
502
573
|
stateChangeSubject = new Subject();
|
|
503
574
|
/** Whether an editor is currently registered with this service. */
|
|
504
|
-
hasEditor = computed(() => this.editorRef() !== null, ...(ngDevMode ? [{ debugName: "hasEditor" }] : []));
|
|
575
|
+
hasEditor = computed(() => this.editorRef() !== null, ...(ngDevMode ? [{ debugName: "hasEditor" }] : /* istanbul ignore next */ []));
|
|
505
576
|
/** Observable stream of state change events from the editor. */
|
|
506
577
|
stateChanges$ = this.stateChangeSubject.asObservable();
|
|
507
578
|
stateChangeSub = null;
|
|
@@ -548,10 +619,10 @@ class NotectlEditorService {
|
|
|
548
619
|
dispatch(tr) {
|
|
549
620
|
this.editorRef()?.dispatch(tr);
|
|
550
621
|
}
|
|
551
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
552
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
622
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NotectlEditorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
623
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NotectlEditorService });
|
|
553
624
|
}
|
|
554
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
625
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NotectlEditorService, decorators: [{
|
|
555
626
|
type: Injectable
|
|
556
627
|
}], ctorParameters: () => [] });
|
|
557
628
|
|