@notectl/angular 0.0.4 → 1.0.0
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/package.json +41 -44
- package/README.md +0 -141
- package/src/index.ts +0 -6
- package/src/lib/notectl-editor.component.ts +0 -261
- package/src/lib/notectl-editor.directive.ts +0 -156
- package/src/lib/notectl-editor.module.ts +0 -14
- package/src/public-api.ts +0 -7
package/package.json
CHANGED
|
@@ -1,58 +1,55 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notectl/angular",
|
|
3
|
-
"version": "0.0
|
|
4
|
-
"description": "Angular
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.mjs",
|
|
12
|
-
"require": "./dist/index.js"
|
|
13
|
-
}
|
|
14
|
-
},
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Angular integration for the notectl rich text editor. Provides a standalone component, reactive forms support, and DI service.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"module": "fesm2022/notectl-angular.mjs",
|
|
8
|
+
"typings": "types/notectl-angular.d.ts",
|
|
9
|
+
"sideEffects": false,
|
|
15
10
|
"files": [
|
|
16
11
|
"dist",
|
|
17
|
-
"src",
|
|
18
12
|
"README.md"
|
|
19
13
|
],
|
|
20
|
-
"scripts": {
|
|
21
|
-
"build": "ng-packagr -p ng-package.json",
|
|
22
|
-
"test": "echo \"No tests yet\"",
|
|
23
|
-
"lint": "eslint src --ext .ts",
|
|
24
|
-
"typecheck": "tsc --noEmit"
|
|
25
|
-
},
|
|
26
14
|
"keywords": [
|
|
15
|
+
"angular",
|
|
16
|
+
"rich-text-editor",
|
|
27
17
|
"notectl",
|
|
18
|
+
"web-component",
|
|
28
19
|
"editor",
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
20
|
+
"wysiwyg",
|
|
21
|
+
"reactive-forms",
|
|
22
|
+
"signals"
|
|
32
23
|
],
|
|
33
|
-
"author": "Samuel Abramov",
|
|
34
|
-
"license": "MIT",
|
|
35
|
-
"peerDependencies": {
|
|
36
|
-
"@angular/common": ">=14.0.0",
|
|
37
|
-
"@angular/core": ">=14.0.0",
|
|
38
|
-
"@angular/forms": ">=14.0.0",
|
|
39
|
-
"@notectl/core": "*"
|
|
40
|
-
},
|
|
41
|
-
"devDependencies": {
|
|
42
|
-
"@angular/common": "^17.0.0",
|
|
43
|
-
"@angular/compiler": "^17.0.0",
|
|
44
|
-
"@angular/compiler-cli": "^17.0.0",
|
|
45
|
-
"@angular/core": "^17.0.0",
|
|
46
|
-
"@angular/forms": "^17.0.0",
|
|
47
|
-
"ng-packagr": "^17.0.0",
|
|
48
|
-
"typescript": "^5.3.0"
|
|
49
|
-
},
|
|
50
24
|
"repository": {
|
|
51
25
|
"type": "git",
|
|
52
|
-
"url": "
|
|
53
|
-
"directory": "packages/
|
|
26
|
+
"url": "https://github.com/Samyssmile/notectl.git",
|
|
27
|
+
"directory": "packages/angular"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/Samyssmile/notectl",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/Samyssmile/notectl/issues"
|
|
54
32
|
},
|
|
55
|
-
"
|
|
56
|
-
"
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"tslib": "^2.3.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@angular/core": ">=21.0.0",
|
|
38
|
+
"@angular/forms": ">=21.0.0",
|
|
39
|
+
"@notectl/core": "^1.0.0",
|
|
40
|
+
"rxjs": ">=7.0.0"
|
|
41
|
+
},
|
|
42
|
+
"exports": {
|
|
43
|
+
"./package.json": {
|
|
44
|
+
"default": "./package.json"
|
|
45
|
+
},
|
|
46
|
+
".": {
|
|
47
|
+
"types": "./types/notectl-angular.d.ts",
|
|
48
|
+
"default": "./fesm2022/notectl-angular.mjs"
|
|
49
|
+
},
|
|
50
|
+
"./testing": {
|
|
51
|
+
"types": "./types/notectl-angular-testing.d.ts",
|
|
52
|
+
"default": "./fesm2022/notectl-angular-testing.mjs"
|
|
53
|
+
}
|
|
57
54
|
}
|
|
58
|
-
}
|
|
55
|
+
}
|
package/README.md
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
# @notectl/angular
|
|
2
|
-
|
|
3
|
-
Angular adapter for NotectlEditor - a framework-agnostic rich text editor built on Web Components.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @notectl/angular @notectl/core
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Usage
|
|
12
|
-
|
|
13
|
-
### Import Module
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import { NgModule } from '@angular/core';
|
|
17
|
-
import { BrowserModule } from '@angular/platform-browser';
|
|
18
|
-
import { NotectlEditorModule } from '@notectl/angular';
|
|
19
|
-
import { AppComponent } from './app.component';
|
|
20
|
-
|
|
21
|
-
@NgModule({
|
|
22
|
-
declarations: [AppComponent],
|
|
23
|
-
imports: [
|
|
24
|
-
BrowserModule,
|
|
25
|
-
NotectlEditorModule
|
|
26
|
-
],
|
|
27
|
-
providers: [],
|
|
28
|
-
bootstrap: [AppComponent]
|
|
29
|
-
})
|
|
30
|
-
export class AppModule { }
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Component Usage
|
|
34
|
-
|
|
35
|
-
```typescript
|
|
36
|
-
import { Component } from '@angular/core';
|
|
37
|
-
import type { EditorAPI } from '@notectl/core';
|
|
38
|
-
|
|
39
|
-
@Component({
|
|
40
|
-
selector: 'app-root',
|
|
41
|
-
template: `
|
|
42
|
-
<notectl-editor
|
|
43
|
-
[content]="content"
|
|
44
|
-
[placeholder]="'Start writing...'"
|
|
45
|
-
[readOnly]="false"
|
|
46
|
-
(contentChange)="onContentChange($event)"
|
|
47
|
-
(ready)="onReady($event)"
|
|
48
|
-
></notectl-editor>
|
|
49
|
-
`
|
|
50
|
-
})
|
|
51
|
-
export class AppComponent {
|
|
52
|
-
content = { type: 'doc', content: [] };
|
|
53
|
-
|
|
54
|
-
onContentChange(content: unknown): void {
|
|
55
|
-
console.log('Content changed:', content);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
onReady(editor: EditorAPI): void {
|
|
59
|
-
console.log('Editor ready:', editor);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
### Reactive Forms Integration
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
import { Component } from '@angular/core';
|
|
68
|
-
import { FormControl, FormGroup } from '@angular/forms';
|
|
69
|
-
|
|
70
|
-
@Component({
|
|
71
|
-
selector: 'app-editor-form',
|
|
72
|
-
template: `
|
|
73
|
-
<form [formGroup]="form">
|
|
74
|
-
<notectl-editor formControlName="content"></notectl-editor>
|
|
75
|
-
</form>
|
|
76
|
-
`
|
|
77
|
-
})
|
|
78
|
-
export class EditorFormComponent {
|
|
79
|
-
form = new FormGroup({
|
|
80
|
-
content: new FormControl(null)
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Directive Usage
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
import { Component } from '@angular/core';
|
|
89
|
-
|
|
90
|
-
@Component({
|
|
91
|
-
selector: 'app-editor',
|
|
92
|
-
template: `
|
|
93
|
-
<div
|
|
94
|
-
notectl-editor
|
|
95
|
-
[content]="content"
|
|
96
|
-
(contentChange)="onContentChange($event)"
|
|
97
|
-
></div>
|
|
98
|
-
`
|
|
99
|
-
})
|
|
100
|
-
export class EditorComponent {
|
|
101
|
-
content = { type: 'doc', content: [] };
|
|
102
|
-
|
|
103
|
-
onContentChange(content: unknown): void {
|
|
104
|
-
console.log('Content changed:', content);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## API
|
|
110
|
-
|
|
111
|
-
### Component Props
|
|
112
|
-
|
|
113
|
-
- `debug?: boolean` - Enable debug mode
|
|
114
|
-
- `content?: string | object` - Initial editor content
|
|
115
|
-
- `placeholder?: string` - Placeholder text
|
|
116
|
-
- `readOnly?: boolean` - Read-only mode
|
|
117
|
-
- `accessibility?: object` - Accessibility configuration
|
|
118
|
-
- `i18n?: object` - Internationalization settings
|
|
119
|
-
- `theme?: object` - Theme configuration
|
|
120
|
-
|
|
121
|
-
### Component Events
|
|
122
|
-
|
|
123
|
-
- `contentChange: EventEmitter<unknown>` - Emitted when content changes
|
|
124
|
-
- `selectionChange: EventEmitter<unknown>` - Emitted when selection changes
|
|
125
|
-
- `editorFocus: EventEmitter<void>` - Emitted when editor gains focus
|
|
126
|
-
- `editorBlur: EventEmitter<void>` - Emitted when editor loses focus
|
|
127
|
-
- `ready: EventEmitter<EditorAPI>` - Emitted when editor is ready
|
|
128
|
-
- `error: EventEmitter<Error>` - Emitted when an error occurs
|
|
129
|
-
|
|
130
|
-
### Component Methods
|
|
131
|
-
|
|
132
|
-
- `getContent(): unknown` - Get current content
|
|
133
|
-
- `setContent(content: unknown): void` - Set content
|
|
134
|
-
- `getState(): unknown` - Get editor state
|
|
135
|
-
- `executeCommand(command: string, ...args: unknown[]): void` - Execute command
|
|
136
|
-
- `registerPlugin(plugin: unknown): void` - Register a plugin
|
|
137
|
-
- `unregisterPlugin(pluginId: string): void` - Unregister a plugin
|
|
138
|
-
|
|
139
|
-
## License
|
|
140
|
-
|
|
141
|
-
MIT
|
package/src/index.ts
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Angular component wrapper for NotectlEditor
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
Component,
|
|
7
|
-
ElementRef,
|
|
8
|
-
EventEmitter,
|
|
9
|
-
Input,
|
|
10
|
-
OnDestroy,
|
|
11
|
-
OnInit,
|
|
12
|
-
Output,
|
|
13
|
-
ViewChild,
|
|
14
|
-
ViewEncapsulation,
|
|
15
|
-
forwardRef,
|
|
16
|
-
} from '@angular/core';
|
|
17
|
-
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
18
|
-
import { NotectlEditor as NotectlEditorCore } from '@notectl/core';
|
|
19
|
-
import type { EditorConfig, EditorAPI } from '@notectl/core';
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* NotectlEditor Angular component
|
|
23
|
-
*/
|
|
24
|
-
@Component({
|
|
25
|
-
selector: 'notectl-editor',
|
|
26
|
-
template: '<div #container [attr.data-notectl-angular-wrapper]="true"></div>',
|
|
27
|
-
styles: [':host { display: block; }'],
|
|
28
|
-
encapsulation: ViewEncapsulation.None,
|
|
29
|
-
providers: [
|
|
30
|
-
{
|
|
31
|
-
provide: NG_VALUE_ACCESSOR,
|
|
32
|
-
useExisting: forwardRef(() => NotectlEditorComponent),
|
|
33
|
-
multi: true,
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
})
|
|
37
|
-
export class NotectlEditorComponent implements OnInit, OnDestroy, ControlValueAccessor {
|
|
38
|
-
@ViewChild('container', { static: true }) containerRef!: ElementRef<HTMLDivElement>;
|
|
39
|
-
|
|
40
|
-
/** Debug mode */
|
|
41
|
-
@Input() debug?: boolean;
|
|
42
|
-
|
|
43
|
-
/** Initial content */
|
|
44
|
-
@Input() content?: string | object;
|
|
45
|
-
|
|
46
|
-
/** Placeholder text */
|
|
47
|
-
@Input() placeholder?: string;
|
|
48
|
-
|
|
49
|
-
/** Read-only mode */
|
|
50
|
-
@Input() readOnly?: boolean;
|
|
51
|
-
|
|
52
|
-
/** Accessibility configuration */
|
|
53
|
-
@Input() accessibility?: EditorConfig['accessibility'];
|
|
54
|
-
|
|
55
|
-
/** Internationalization configuration */
|
|
56
|
-
@Input() i18n?: EditorConfig['i18n'];
|
|
57
|
-
|
|
58
|
-
/** Theme configuration */
|
|
59
|
-
@Input() theme?: EditorConfig['theme'];
|
|
60
|
-
|
|
61
|
-
/** Custom class name */
|
|
62
|
-
@Input() className?: string;
|
|
63
|
-
|
|
64
|
-
/** Emitted when content changes */
|
|
65
|
-
@Output() contentChange = new EventEmitter<unknown>();
|
|
66
|
-
|
|
67
|
-
/** Emitted when selection changes */
|
|
68
|
-
@Output() selectionChange = new EventEmitter<unknown>();
|
|
69
|
-
|
|
70
|
-
/** Emitted when editor gains focus */
|
|
71
|
-
@Output() editorFocus = new EventEmitter<void>();
|
|
72
|
-
|
|
73
|
-
/** Emitted when editor loses focus */
|
|
74
|
-
@Output() editorBlur = new EventEmitter<void>();
|
|
75
|
-
|
|
76
|
-
/** Emitted when editor is ready */
|
|
77
|
-
@Output() ready = new EventEmitter<EditorAPI>();
|
|
78
|
-
|
|
79
|
-
/** Emitted when an error occurs */
|
|
80
|
-
@Output() error = new EventEmitter<Error>();
|
|
81
|
-
|
|
82
|
-
private editor: NotectlEditorCore | null = null;
|
|
83
|
-
private onChange: (value: unknown) => void = () => {};
|
|
84
|
-
private onTouched: () => void = () => {};
|
|
85
|
-
|
|
86
|
-
ngOnInit(): void {
|
|
87
|
-
this.initEditor();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
ngOnDestroy(): void {
|
|
91
|
-
this.destroyEditor();
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Initialize the editor instance
|
|
96
|
-
*/
|
|
97
|
-
private initEditor(): void {
|
|
98
|
-
if (!this.containerRef?.nativeElement) {
|
|
99
|
-
console.error('NotectlEditor: Container element not found');
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Create editor instance
|
|
104
|
-
this.editor = document.createElement('notectl-editor') as NotectlEditorCore;
|
|
105
|
-
|
|
106
|
-
// Configure editor
|
|
107
|
-
const config: EditorConfig = {
|
|
108
|
-
debug: this.debug,
|
|
109
|
-
content: this.content,
|
|
110
|
-
placeholder: this.placeholder,
|
|
111
|
-
readOnly: this.readOnly,
|
|
112
|
-
accessibility: this.accessibility,
|
|
113
|
-
i18n: this.i18n,
|
|
114
|
-
theme: this.theme,
|
|
115
|
-
};
|
|
116
|
-
this.editor.configure(config);
|
|
117
|
-
|
|
118
|
-
// Attach event listeners
|
|
119
|
-
this.editor.on('content-change', (data) => {
|
|
120
|
-
const eventData = data as { content?: unknown };
|
|
121
|
-
const content = eventData.content;
|
|
122
|
-
this.contentChange.emit(content);
|
|
123
|
-
this.onChange(content);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
this.editor.on('selection-change', (data) => {
|
|
127
|
-
const eventData = data as { selection?: unknown };
|
|
128
|
-
this.selectionChange.emit(eventData.selection);
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
this.editor.on('focus', () => {
|
|
132
|
-
this.editorFocus.emit();
|
|
133
|
-
this.onTouched();
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
this.editor.on('blur', () => {
|
|
137
|
-
this.editorBlur.emit();
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
this.editor.on('ready', () => {
|
|
141
|
-
this.ready.emit(this.getEditorAPI());
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
this.editor.on('error', (data) => {
|
|
145
|
-
const eventData = data as { error?: Error };
|
|
146
|
-
if (eventData.error) {
|
|
147
|
-
this.error.emit(eventData.error);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// Mount editor
|
|
152
|
-
this.containerRef.nativeElement.appendChild(this.editor);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Destroy the editor instance
|
|
157
|
-
*/
|
|
158
|
-
private destroyEditor(): void {
|
|
159
|
-
if (this.editor) {
|
|
160
|
-
this.editor.destroy();
|
|
161
|
-
if (this.containerRef?.nativeElement?.contains(this.editor)) {
|
|
162
|
-
this.containerRef.nativeElement.removeChild(this.editor);
|
|
163
|
-
}
|
|
164
|
-
this.editor = null;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Update editor configuration
|
|
170
|
-
*/
|
|
171
|
-
private updateConfig(): void {
|
|
172
|
-
if (!this.editor) return;
|
|
173
|
-
|
|
174
|
-
const config: EditorConfig = {
|
|
175
|
-
debug: this.debug,
|
|
176
|
-
content: this.content,
|
|
177
|
-
placeholder: this.placeholder,
|
|
178
|
-
readOnly: this.readOnly,
|
|
179
|
-
accessibility: this.accessibility,
|
|
180
|
-
i18n: this.i18n,
|
|
181
|
-
theme: this.theme,
|
|
182
|
-
};
|
|
183
|
-
this.editor.configure(config);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Get editor API
|
|
188
|
-
*/
|
|
189
|
-
private getEditorAPI(): EditorAPI {
|
|
190
|
-
if (!this.editor) {
|
|
191
|
-
throw new Error('Editor not initialized');
|
|
192
|
-
}
|
|
193
|
-
return this.editor as EditorAPI;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
// ControlValueAccessor implementation
|
|
197
|
-
writeValue(value: unknown): void {
|
|
198
|
-
if (this.editor && typeof value === 'string') {
|
|
199
|
-
this.editor.setContent(value);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
registerOnChange(fn: (value: unknown) => void): void {
|
|
204
|
-
this.onChange = fn;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
registerOnTouched(fn: () => void): void {
|
|
208
|
-
this.onTouched = fn;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
setDisabledState(isDisabled: boolean): void {
|
|
212
|
-
if (this.editor) {
|
|
213
|
-
this.editor.configure({ readOnly: isDisabled });
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Public API methods
|
|
218
|
-
/**
|
|
219
|
-
* Get current editor content
|
|
220
|
-
*/
|
|
221
|
-
public getContent(): unknown {
|
|
222
|
-
return this.editor?.getContent();
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Set editor content
|
|
227
|
-
*/
|
|
228
|
-
public setContent(content: unknown): void {
|
|
229
|
-
if (this.editor && typeof content === 'string') {
|
|
230
|
-
this.editor.setContent(content);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* Get current editor state
|
|
236
|
-
*/
|
|
237
|
-
public getState(): unknown {
|
|
238
|
-
return this.editor?.getState();
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Execute an editor command
|
|
243
|
-
*/
|
|
244
|
-
public executeCommand(command: string, ...args: unknown[]): void {
|
|
245
|
-
this.editor?.executeCommand(command, ...args);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Register a plugin
|
|
250
|
-
*/
|
|
251
|
-
public registerPlugin(plugin: unknown): void {
|
|
252
|
-
this.editor?.registerPlugin(plugin as any);
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Unregister a plugin
|
|
257
|
-
*/
|
|
258
|
-
public unregisterPlugin(pluginId: string): void {
|
|
259
|
-
this.editor?.unregisterPlugin(pluginId);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Angular directive for NotectlEditor (optional alternative to component)
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
Directive,
|
|
7
|
-
ElementRef,
|
|
8
|
-
EventEmitter,
|
|
9
|
-
Input,
|
|
10
|
-
OnDestroy,
|
|
11
|
-
OnInit,
|
|
12
|
-
Output,
|
|
13
|
-
forwardRef,
|
|
14
|
-
} from '@angular/core';
|
|
15
|
-
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
|
|
16
|
-
import { NotectlEditor as NotectlEditorCore } from '@notectl/core';
|
|
17
|
-
import type { EditorConfig, EditorAPI } from '@notectl/core';
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Directive to use NotectlEditor on any element
|
|
21
|
-
* Usage: <div notectl-editor [content]="content"></div>
|
|
22
|
-
*/
|
|
23
|
-
@Directive({
|
|
24
|
-
selector: '[notectl-editor]',
|
|
25
|
-
providers: [
|
|
26
|
-
{
|
|
27
|
-
provide: NG_VALUE_ACCESSOR,
|
|
28
|
-
useExisting: forwardRef(() => NotectlEditorDirective),
|
|
29
|
-
multi: true,
|
|
30
|
-
},
|
|
31
|
-
],
|
|
32
|
-
})
|
|
33
|
-
export class NotectlEditorDirective implements OnInit, OnDestroy, ControlValueAccessor {
|
|
34
|
-
@Input() debug?: boolean;
|
|
35
|
-
@Input() content?: string | object;
|
|
36
|
-
@Input() placeholder?: string;
|
|
37
|
-
@Input() readOnly?: boolean;
|
|
38
|
-
@Input() accessibility?: EditorConfig['accessibility'];
|
|
39
|
-
@Input() i18n?: EditorConfig['i18n'];
|
|
40
|
-
@Input() theme?: EditorConfig['theme'];
|
|
41
|
-
|
|
42
|
-
@Output() contentChange = new EventEmitter<unknown>();
|
|
43
|
-
@Output() selectionChange = new EventEmitter<unknown>();
|
|
44
|
-
@Output() editorFocus = new EventEmitter<void>();
|
|
45
|
-
@Output() editorBlur = new EventEmitter<void>();
|
|
46
|
-
@Output() ready = new EventEmitter<EditorAPI>();
|
|
47
|
-
@Output() error = new EventEmitter<Error>();
|
|
48
|
-
|
|
49
|
-
private editor: NotectlEditorCore | null = null;
|
|
50
|
-
private onChange: (value: unknown) => void = () => {};
|
|
51
|
-
private onTouched: () => void = () => {};
|
|
52
|
-
|
|
53
|
-
constructor(private elementRef: ElementRef<HTMLElement>) {}
|
|
54
|
-
|
|
55
|
-
ngOnInit(): void {
|
|
56
|
-
this.initEditor();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
ngOnDestroy(): void {
|
|
60
|
-
this.destroyEditor();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private initEditor(): void {
|
|
64
|
-
const hostElement = this.elementRef.nativeElement;
|
|
65
|
-
|
|
66
|
-
// Create editor instance
|
|
67
|
-
this.editor = document.createElement('notectl-editor') as NotectlEditorCore;
|
|
68
|
-
|
|
69
|
-
// Configure editor
|
|
70
|
-
const config: EditorConfig = {
|
|
71
|
-
debug: this.debug,
|
|
72
|
-
content: this.content,
|
|
73
|
-
placeholder: this.placeholder,
|
|
74
|
-
readOnly: this.readOnly,
|
|
75
|
-
accessibility: this.accessibility,
|
|
76
|
-
i18n: this.i18n,
|
|
77
|
-
theme: this.theme,
|
|
78
|
-
};
|
|
79
|
-
this.editor.configure(config);
|
|
80
|
-
|
|
81
|
-
// Attach event listeners
|
|
82
|
-
this.editor.on('content-change', (data) => {
|
|
83
|
-
const eventData = data as { content?: unknown };
|
|
84
|
-
const content = eventData.content;
|
|
85
|
-
this.contentChange.emit(content);
|
|
86
|
-
this.onChange(content);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
this.editor.on('selection-change', (data) => {
|
|
90
|
-
const eventData = data as { selection?: unknown };
|
|
91
|
-
this.selectionChange.emit(eventData.selection);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
this.editor.on('focus', () => {
|
|
95
|
-
this.editorFocus.emit();
|
|
96
|
-
this.onTouched();
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
this.editor.on('blur', () => {
|
|
100
|
-
this.editorBlur.emit();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
this.editor.on('ready', () => {
|
|
104
|
-
this.ready.emit(this.getEditorAPI());
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
this.editor.on('error', (data) => {
|
|
108
|
-
const eventData = data as { error?: Error };
|
|
109
|
-
if (eventData.error) {
|
|
110
|
-
this.error.emit(eventData.error);
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
// Mount editor
|
|
115
|
-
hostElement.appendChild(this.editor);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
private destroyEditor(): void {
|
|
119
|
-
if (this.editor) {
|
|
120
|
-
this.editor.destroy();
|
|
121
|
-
const hostElement = this.elementRef.nativeElement;
|
|
122
|
-
if (hostElement.contains(this.editor)) {
|
|
123
|
-
hostElement.removeChild(this.editor);
|
|
124
|
-
}
|
|
125
|
-
this.editor = null;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
private getEditorAPI(): EditorAPI {
|
|
130
|
-
if (!this.editor) {
|
|
131
|
-
throw new Error('Editor not initialized');
|
|
132
|
-
}
|
|
133
|
-
return this.editor as EditorAPI;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ControlValueAccessor implementation
|
|
137
|
-
writeValue(value: unknown): void {
|
|
138
|
-
if (this.editor && typeof value === 'string') {
|
|
139
|
-
this.editor.setContent(value);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
registerOnChange(fn: (value: unknown) => void): void {
|
|
144
|
-
this.onChange = fn;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
registerOnTouched(fn: () => void): void {
|
|
148
|
-
this.onTouched = fn;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
setDisabledState(isDisabled: boolean): void {
|
|
152
|
-
if (this.editor) {
|
|
153
|
-
this.editor.configure({ readOnly: isDisabled });
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Angular module for NotectlEditor
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { NgModule } from '@angular/core';
|
|
6
|
-
import { CommonModule } from '@angular/common';
|
|
7
|
-
import { NotectlEditorComponent } from './notectl-editor.component';
|
|
8
|
-
|
|
9
|
-
@NgModule({
|
|
10
|
-
declarations: [NotectlEditorComponent],
|
|
11
|
-
imports: [CommonModule],
|
|
12
|
-
exports: [NotectlEditorComponent],
|
|
13
|
-
})
|
|
14
|
-
export class NotectlEditorModule {}
|