ngx-lexical-editor 1.0.0 → 1.1.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/README.md +83 -46
- package/fesm2022/ngx-lexical-editor.mjs +111 -31
- package/fesm2022/ngx-lexical-editor.mjs.map +1 -1
- package/package.json +1 -1
- package/types/ngx-lexical-editor.d.ts +27 -32
package/README.md
CHANGED
|
@@ -1,64 +1,101 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ngx-lexical-editor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An Angular wrapper for the powerful [Lexical](https://lexical.dev/) text editor framework from Meta. This library aims to provide a seamless integration of Lexical's extensible, accessible, and high-performance text editing capabilities directly into your Angular applications.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
Angular
|
|
7
|
+
- 🚀 **Angular Integrated:** Built specifically for Angular, using modern features like standalone components and signals.
|
|
8
|
+
- 🧱 **Rich Text Out-of-the-Box:** Includes support for headings, bold, italic, underline, lists (bullet, number, check), code blocks, and tables.
|
|
9
|
+
- 📤 **Reactive Outputs:** Easily react to editor changes with `jsonDataChanged` (Serialized Editor State) and `htmlContentChanged` (HTML representation) outputs.
|
|
10
|
+
- 🎨 **Headless Core with Extensibility:** Leverage Lexical's core capabilities while maintaining full control over your UI and styling. Includes a built-in toolbar for common formatting tasks.
|
|
11
|
+
- ♿️ **Accessible:** Integrates with `@angular/aria` for improved toolbar accessibility.
|
|
12
|
+
- 👁️ **Preview Component:** Includes a handy `LexicalPreviewComponent` to safely and easily render the generated HTML output.
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
ng generate component component-name
|
|
11
|
-
```
|
|
14
|
+
## Installation
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
Install the library and its peer dependencies using your preferred package manager (npm, yarn, or pnpm):
|
|
14
17
|
|
|
15
18
|
```bash
|
|
16
|
-
|
|
19
|
+
npm install ngx-lexical-editor @angular/aria lexical @lexical/rich-text @lexical/selection @lexical/list @lexical/code @lexical/table @lexical/html prismjs
|
|
17
20
|
```
|
|
18
21
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
> **Note:** Ensure you have the required peer dependencies installed, particularly the core `lexical` package and the necessary `@lexical/*` plugins.
|
|
23
|
+
|
|
24
|
+
## Quickstart & Example Usage
|
|
25
|
+
|
|
26
|
+
Here's a basic example of how to use the editor and preview components in your application.
|
|
27
|
+
|
|
28
|
+
### 1. Import the Component
|
|
29
|
+
|
|
30
|
+
Import `LexicalEditorComponent` into your standalone component or NgModule.
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { Component, signal } from '@angular/core';
|
|
34
|
+
import { LexicalEditorComponent, LexicalPreviewComponent } from 'ngx-lexical-editor';
|
|
35
|
+
import { SerializedEditorState } from 'lexical';
|
|
36
|
+
|
|
37
|
+
@Component({
|
|
38
|
+
selector: 'app-my-editor-feature',
|
|
39
|
+
standalone: true,
|
|
40
|
+
imports: [LexicalEditorComponent, LexicalPreviewComponent],
|
|
41
|
+
template: `
|
|
42
|
+
<div class="editor-container">
|
|
43
|
+
<h2>Edit Content</h2>
|
|
44
|
+
<lexical-editor
|
|
45
|
+
(jsonDataChanged)="onDataChange($event)"
|
|
46
|
+
(htmlContentChanged)="onHtmlChange($event)">
|
|
47
|
+
</lexical-editor>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div class="preview-container">
|
|
51
|
+
<h2>Preview</h2>
|
|
52
|
+
<lexical-preview [html]="currentHtml()"></lexical-preview>
|
|
53
|
+
</div>
|
|
54
|
+
`,
|
|
55
|
+
styles: [`
|
|
56
|
+
.editor-container, .preview-container {
|
|
57
|
+
margin-bottom: 2rem;
|
|
58
|
+
border: 1px solid #ccc;
|
|
59
|
+
padding: 1rem;
|
|
60
|
+
border-radius: 4px;
|
|
61
|
+
}
|
|
62
|
+
`]
|
|
63
|
+
})
|
|
64
|
+
export class MyEditorFeatureComponent {
|
|
65
|
+
currentHtml = signal<string>('');
|
|
66
|
+
currentJson = signal<SerializedEditorState | null>(null);
|
|
67
|
+
|
|
68
|
+
onDataChange(state: SerializedEditorState) {
|
|
69
|
+
console.log('Editor State (JSON):', state);
|
|
70
|
+
this.currentJson.set(state);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
onHtmlChange(html: string) {
|
|
74
|
+
console.log('Editor Content (HTML):', html);
|
|
75
|
+
this.currentHtml.set(html);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
25
78
|
```
|
|
26
79
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
### Publishing the Library
|
|
30
|
-
|
|
31
|
-
Once the project is built, you can publish your library by following these steps:
|
|
32
|
-
|
|
33
|
-
1. Navigate to the `dist` directory:
|
|
80
|
+
## Contributing
|
|
34
81
|
|
|
35
|
-
|
|
36
|
-
cd dist/ngx-lexical-editor
|
|
37
|
-
```
|
|
82
|
+
We welcome contributions! If you'd like to help improve `ngx-lexical-editor`, please follow these guidelines:
|
|
38
83
|
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npm publish
|
|
42
|
-
```
|
|
84
|
+
### Setup for Local Development
|
|
43
85
|
|
|
44
|
-
|
|
86
|
+
1. **Fork and Clone:** Fork the repository on GitHub and clone it locally.
|
|
87
|
+
2. **Install Dependencies:** Run `pnpm install` (or your preferred package manager) in the workspace root.
|
|
88
|
+
3. **Build the Library:** Run `npm run build` or `ng build ngx-lexical-editor` to build the library. You can also run `npm run watch` to automatically rebuild on file changes.
|
|
89
|
+
4. **Test your changes:** You can test your changes by linking the built library to a test application or running the workspace's tests using `npm test`.
|
|
45
90
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
ng test
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
## Running end-to-end tests
|
|
53
|
-
|
|
54
|
-
For end-to-end (e2e) testing, run:
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
ng e2e
|
|
58
|
-
```
|
|
91
|
+
### Submitting a Pull Request
|
|
59
92
|
|
|
60
|
-
|
|
93
|
+
1. **Create a Branch:** Create a new branch for your feature or bug fix (`git checkout -b feature/my-new-feature`).
|
|
94
|
+
2. **Make Changes:** Implement your feature, fix the bug, or improve documentation. Ensure your code follows the existing style and conventions.
|
|
95
|
+
3. **Commit:** Write clear, concise commit messages.
|
|
96
|
+
4. **Push:** Push your branch to your fork (`git push origin feature/my-new-feature`).
|
|
97
|
+
5. **Open a PR:** Open a Pull Request against the `main` branch of the original repository. Describe your changes clearly in the PR description.
|
|
61
98
|
|
|
62
|
-
##
|
|
99
|
+
## License
|
|
63
100
|
|
|
64
|
-
|
|
101
|
+
MIT License
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, PLATFORM_ID, signal,
|
|
2
|
+
import { inject, PLATFORM_ID, signal, computed, output, effect, ViewChild, Component, input } from '@angular/core';
|
|
3
3
|
import { isPlatformBrowser, CommonModule } from '@angular/common';
|
|
4
4
|
import { ParagraphNode, TextNode, createEditor, FORMAT_TEXT_COMMAND, $getSelection, $isRangeSelection, $createParagraphNode } from 'lexical';
|
|
5
5
|
import { HeadingNode, QuoteNode, registerRichText, $createHeadingNode } from '@lexical/rich-text';
|
|
6
6
|
import { ListNode, ListItemNode, $insertList } from '@lexical/list';
|
|
7
7
|
import { CodeNode, CodeHighlightNode, $createCodeNode } from '@lexical/code';
|
|
8
8
|
import { TableNode, TableRowNode, TableCellNode, INSERT_TABLE_COMMAND } from '@lexical/table';
|
|
9
|
-
import { $setBlocksType } from '@lexical/selection';
|
|
10
9
|
import { $generateHtmlFromNodes } from '@lexical/html';
|
|
10
|
+
import { $setBlocksType } from '@lexical/selection';
|
|
11
|
+
import { Toolbar, ToolbarWidgetGroup, ToolbarWidget } from '@angular/aria/toolbar';
|
|
11
12
|
import { DomSanitizer } from '@angular/platform-browser';
|
|
12
13
|
|
|
13
14
|
class EditorService {
|
|
@@ -18,7 +19,6 @@ class EditorService {
|
|
|
18
19
|
const editorConfig = {
|
|
19
20
|
namespace: 'NgxLexicalEditor',
|
|
20
21
|
theme: {
|
|
21
|
-
// Your existing theme...
|
|
22
22
|
paragraph: 'lexical-paragraph',
|
|
23
23
|
heading: {
|
|
24
24
|
h1: 'lexical-h1',
|
|
@@ -52,8 +52,8 @@ class EditorService {
|
|
|
52
52
|
TableNode,
|
|
53
53
|
TableRowNode,
|
|
54
54
|
TableCellNode,
|
|
55
|
-
ParagraphNode,
|
|
56
|
-
TextNode,
|
|
55
|
+
ParagraphNode,
|
|
56
|
+
TextNode,
|
|
57
57
|
],
|
|
58
58
|
onError: (error) => {
|
|
59
59
|
console.error('Lexical Error:', error);
|
|
@@ -64,29 +64,59 @@ class EditorService {
|
|
|
64
64
|
this.initEditor(editorConfig);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
/**
|
|
68
|
+
* JSON Signal of the editor's content
|
|
69
|
+
*/
|
|
70
|
+
get getJSON() {
|
|
71
|
+
return computed(() => {
|
|
72
|
+
const state = this.editorState();
|
|
73
|
+
return state
|
|
74
|
+
? state.toJSON()
|
|
75
|
+
: {
|
|
76
|
+
root: {
|
|
77
|
+
children: [],
|
|
78
|
+
direction: null,
|
|
79
|
+
format: '',
|
|
80
|
+
indent: 0,
|
|
81
|
+
type: 'root',
|
|
82
|
+
version: 1,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Returns the editor's content as an HTML string. As a signal
|
|
89
|
+
*/
|
|
90
|
+
get getHTML() {
|
|
91
|
+
return computed(() => {
|
|
92
|
+
const state = this.editorState();
|
|
93
|
+
if (!state || !this.editor) {
|
|
94
|
+
return '';
|
|
95
|
+
}
|
|
96
|
+
let html = '';
|
|
97
|
+
this.editor.getEditorState().read(() => {
|
|
98
|
+
html = $generateHtmlFromNodes(this.editor);
|
|
99
|
+
});
|
|
100
|
+
return html;
|
|
101
|
+
});
|
|
69
102
|
}
|
|
70
103
|
getEditorState() {
|
|
71
104
|
return this.editorState.asReadonly();
|
|
72
105
|
}
|
|
106
|
+
getEditor() {
|
|
107
|
+
return this.editor;
|
|
108
|
+
}
|
|
73
109
|
async initEditor(editorConfig) {
|
|
74
110
|
this.editor = createEditor(editorConfig);
|
|
75
111
|
this.editor.registerUpdateListener(({ editorState }) => {
|
|
76
112
|
this.editorState.set(editorState);
|
|
77
113
|
});
|
|
78
114
|
}
|
|
79
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: EditorService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
80
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: EditorService, providedIn: 'root' });
|
|
81
115
|
}
|
|
82
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: EditorService, decorators: [{
|
|
83
|
-
type: Injectable,
|
|
84
|
-
args: [{
|
|
85
|
-
providedIn: 'root',
|
|
86
|
-
}]
|
|
87
|
-
}], ctorParameters: () => [] });
|
|
88
116
|
|
|
89
117
|
class LexicalEditorComponent {
|
|
118
|
+
jsonDataChanged = output();
|
|
119
|
+
htmlContentChanged = output();
|
|
90
120
|
editorContent;
|
|
91
121
|
editorService = inject(EditorService);
|
|
92
122
|
editor;
|
|
@@ -95,6 +125,18 @@ class LexicalEditorComponent {
|
|
|
95
125
|
if (this.editor) {
|
|
96
126
|
registerRichText(this.editor);
|
|
97
127
|
}
|
|
128
|
+
effect(() => {
|
|
129
|
+
const json = this.editorService.getJSON();
|
|
130
|
+
if (json) {
|
|
131
|
+
this.jsonDataChanged.emit(json);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
effect(() => {
|
|
135
|
+
const html = this.editorService.getHTML();
|
|
136
|
+
if (html) {
|
|
137
|
+
this.htmlContentChanged.emit(html);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
98
140
|
}
|
|
99
141
|
ngAfterViewInit() {
|
|
100
142
|
this.editor?.setRootElement(this.editorContent.nativeElement);
|
|
@@ -141,48 +183,86 @@ class LexicalEditorComponent {
|
|
|
141
183
|
});
|
|
142
184
|
}
|
|
143
185
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: LexicalEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
144
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.13", type: LexicalEditorComponent, isStandalone: true, selector: "lexical-editor", viewQueries: [{ propertyName: "editorContent", first: true, predicate: ["editorContent"], descendants: true }], ngImport: i0, template: "<div class=\"toolbar\">\n <!-- Inline formats -->\n
|
|
186
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.13", type: LexicalEditorComponent, isStandalone: true, selector: "lexical-editor", outputs: { jsonDataChanged: "jsonDataChanged", htmlContentChanged: "htmlContentChanged" }, viewQueries: [{ propertyName: "editorContent", first: true, predicate: ["editorContent"], descendants: true }], ngImport: i0, template: "<div class=\"toolbar\">\n <div aria-label=\"Text Formatting Tools\" class=\"toolbar\" ngToolbar>\n\n <!-- Inline formats -->\n <div class=\"group\" ngToolbarWidgetGroup>\n <button (click)=\"formatText('bold')\" ngToolbarWidget type=\"button\" value=\"bold\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M266-192v-576h227.95q67.05 0 123.55 41.32Q674-685.35 674-612q0 51-22.5 79.5T609-490.96Q635-479 665-448t30 91q0 91-67.03 128t-125.81 37H266Zm127-118h104.68Q546-310 556-334.5t10-35.5q0-11-10.5-35.5T494-430H393v120Zm0-232h93q33 0 48.5-17.5T550-597q0-24-17.11-39t-44.28-15H393v109Z\" />\n </svg>\n </button>\n <button (click)=\"formatText('italic')\" ngToolbarWidget type=\"button\" value=\"italic\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M216-192v-96h160l124-384H336v-96h408v96H596L472-288h152v96H216Z\" />\n </svg>\n </button>\n <button (click)=\"formatText('underline')\" ngToolbarWidget type=\"button\" value=\"underline\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M240-144v-72h480v72H240Zm91.5-203.4Q279-406.8 279-504.86V-816h97.21v317.09q0 52.85 26.43 85.88Q429.07-380 480.03-380q50.97 0 77.39-33.03 26.41-33.03 26.41-85.88V-816H681v311.14q0 98.06-52.5 157.46Q576-288 480-288t-148.5-59.4Z\" />\n </svg>\n </button>\n </div>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Block formats -->\n <div class=\"group\" ngToolbarWidgetGroup>\n <button (click)=\"formatHeading('h1')\" ngToolbarWidget type=\"button\" value=\"h1\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M192-288v-384h72v156h168v-156h72v384h-72v-156H264v156h-72Zm504 0v-312h-96v-72h168v384h-72Z\" />\n </svg>\n </button>\n <button (click)=\"formatHeading('h2')\" ngToolbarWidget type=\"button\" value=\"h2\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M144-288v-384h72v156h168v-156h72v384h-72v-156H216v156h-72Zm384 0v-156q0-29.7 21.15-50.85Q570.3-516 600-516h144v-84H528v-72h216.26Q774-672 795-650.85q21 21.15 21 50.85v84q0 29.7-21.15 50.85Q773.7-444 744-444H600v84h216v72H528Z\" />\n </svg>\n </button>\n <button (click)=\"formatParagraph()\" ngToolbarWidget type=\"button\" value=\"p\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M384-192v-192q-79.68 0-135.84-56.23-56.16-56.22-56.16-136Q192-656 248.16-712q56.16-56 135.84-56h336v72h-96v504h-72v-504h-96v504h-72Z\" />\n </svg>\n </button>\n </div>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Lists -->\n <div class=\"group\" ngToolbarWidgetGroup>\n <button (click)=\"formatList('bullet')\" ngToolbarWidget type=\"button\" value=\"bullet\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M360-240v-72h456v72H360Zm0-204v-72h456v72H360Zm0-204v-72h456v72H360ZM215.79-204Q186-204 165-225.21t-21-51Q144-306 165.21-327t51-21Q246-348 267-326.79t21 51Q288-246 266.79-225t-51 21Zm0-204Q186-408 165-429.21t-21-51Q144-510 165.21-531t51-21Q246-552 267-530.79t21 51Q288-450 266.79-429t-51 21ZM165-633.21q-21-21.21-21-51T165.21-735q21.21-21 51-21T267-734.79q21 21.21 21 51T266.79-633q-21.21 21-51 21T165-633.21Z\" />\n </svg>\n </button>\n <button (click)=\"formatList('number')\" ngToolbarWidget type=\"button\" value=\"number\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M144-144v-48h96v-24h-48v-48h48v-24h-96v-48h120q10.2 0 17.1 6.9 6.9 6.9 6.9 17.1v48q0 10.2-6.9 17.1-6.9 6.9-17.1 6.9 10.2 0 17.1 6.9 6.9 6.9 6.9 17.1v48q0 10.2-6.9 17.1-6.9 6.9-17.1 6.9H144Zm0-240v-96q0-10.2 6.9-17.1 6.9-6.9 17.1-6.9h72v-24h-96v-48h120q10.2 0 17.1 6.9 6.9 6.9 6.9 17.1v72q0 10.2-6.9 17.1-6.9 6.9-17.1 6.9h-72v24h96v48H144Zm48-240v-144h-48v-48h96v192h-48Zm168 384v-72h456v72H360Zm0-204v-72h456v72H360Zm0-204v-72h456v72H360Z\" />\n </svg>\n </button>\n <button (click)=\"formatList('check')\" ngToolbarWidget type=\"button\" value=\"check\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M232-216 96-352l51-51 84 85 170-170 52 51-221 221Zm0-312L96-664l51-51 85 85 169-170 52 51-221 221Zm296 240v-72h336v72H528Zm0-312v-72h336v72H528Z\" />\n </svg>\n </button>\n </div>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Code Block -->\n <button (click)=\"formatCodeBlock()\" ngToolbarWidget type=\"button\" value=\"code\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M336-240 96-480l240-240 51 51-189 189 189 189-51 51Zm288 0-51-51 189-189-189-189 51-51 240 240-240 240Z\" />\n </svg>\n </button>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Table (Basic insertion) -->\n <button (click)=\"insertTable()\" ngToolbarWidget type=\"button\" value=\"table\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M216-144q-33 0-52.5-19.5T144-216v-528q0-33 19.5-52.5T216-816h528q33 0 52.5 19.5T816-744v528q0 33-19.5 52.5T744-144H216Zm228-228H216v156h228v-156Zm72 0v156h228v-156H516Zm-72-72v-156H216v156h228Zm72 0h228v-156H516v156ZM216-672h528v-72H216v72Z\" />\n </svg>\n </button>\n </div>\n</div>\n<div class=\"lexical-editor-container\">\n <div #editorContent class=\"lexical-content-editable\" contenteditable=\"true\"></div>\n</div>\n", styles: [":host{height:100%;width:100%;--primary-contrast: #333333;--quinary-contrast: #e0e0e0;--septenary-contrast: #f8f9fa;--hot-pink: #ff69b4}[ngToolbar]{margin-bottom:8px;gap:1.5rem;display:flex;padding:.5rem 1rem;border-radius:.5rem;background-color:var(--septenary-contrast)}.group{gap:.5rem;display:flex}.separator{width:1px;align-self:center;height:calc(100% - 1rem);background-color:var(--quinary-contrast)}[ngToolbarWidget]{border:none;cursor:pointer;padding:.5rem;border-radius:4px;display:flex;align-items:center;justify-content:center;background-color:transparent;color:var(--primary-contrast)}[ngToolbarWidget] svg{fill:currentColor}[ngToolbarWidget]:hover{background-color:color-mix(in srgb,var(--primary-contrast) 10%,transparent)}[ngToolbarWidget]:active{background-color:color-mix(in srgb,var(--primary-contrast) 15%,transparent)}[ngToolbarWidget]:focus{outline-offset:-1px;outline:1px solid color-mix(in srgb,var(--hot-pink) 60%,transparent)}[ngToolbarWidget][aria-pressed=true],[ngToolbarWidget][aria-checked=true]{color:color-mix(in srgb,var(--hot-pink) 80%,var(--primary-contrast));background-color:color-mix(in srgb,var(--hot-pink) 10%,transparent)}.divider{color:var(--quinary-contrast);align-self:center}.lexical-editor-container{border:1px solid #ccc;border-radius:4px;position:relative;background-color:#fff}.lexical-content-editable{min-height:300px;padding:16px;outline:none}.lexical-paragraph{margin:0 0 10px}.lexical-bold{font-weight:700}.lexical-italic{font-style:italic}.lexical-underline{text-decoration:underline}.lexical-strikethrough{text-decoration:line-through}.lexical-inline-code{font-family:monospace;background-color:#f0f0f0;padding:2px 4px;border-radius:2px}.lexical-h1{font-size:2em;margin-top:20px;margin-bottom:10px}.lexical-h2{font-size:1.5em;margin-top:15px;margin-bottom:10px}.lexical-h3{font-size:1.17em;margin-top:10px;margin-bottom:10px}.lexical-ul{padding-left:30px;list-style-type:disc}.lexical-ol{padding-left:30px;list-style-type:decimal}.lexical-listitem{margin-bottom:4px}.lexical-code-block{background-color:#282c34;color:#abb2bf;font-family:Fira Code,monospace;display:block;padding:16px;line-height:1.5;font-size:14px;margin:10px 0;overflow-x:auto;border-radius:6px;tab-size:2}.lexical-table{border-collapse:collapse;border-spacing:0;max-width:100%;overflow-y:scroll;table-layout:fixed;width:100%;margin:10px 0}.lexical-table-row{border-bottom:1px solid #ddd}.lexical-table-cell{border:1px solid #ddd;padding:8px;vertical-align:top;text-align:left}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: Toolbar, selector: "[ngToolbar]", inputs: ["orientation", "softDisabled", "disabled", "wrap", "values"], outputs: ["valuesChange"], exportAs: ["ngToolbar"] }, { kind: "directive", type: ToolbarWidgetGroup, selector: "[ngToolbarWidgetGroup]", inputs: ["disabled", "multi"], exportAs: ["ngToolbarWidgetGroup"] }, { kind: "directive", type: ToolbarWidget, selector: "[ngToolbarWidget]", inputs: ["id", "disabled", "value"], exportAs: ["ngToolbarWidget"] }] });
|
|
145
187
|
}
|
|
146
188
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: LexicalEditorComponent, decorators: [{
|
|
147
189
|
type: Component,
|
|
148
|
-
args: [{ selector: 'lexical-editor', standalone: true, imports: [CommonModule], template: "<div class=\"toolbar\">\n <!-- Inline formats -->\n
|
|
149
|
-
}], ctorParameters: () => [], propDecorators: { editorContent: [{
|
|
190
|
+
args: [{ selector: 'lexical-editor', standalone: true, imports: [CommonModule, Toolbar, ToolbarWidgetGroup, ToolbarWidget], template: "<div class=\"toolbar\">\n <div aria-label=\"Text Formatting Tools\" class=\"toolbar\" ngToolbar>\n\n <!-- Inline formats -->\n <div class=\"group\" ngToolbarWidgetGroup>\n <button (click)=\"formatText('bold')\" ngToolbarWidget type=\"button\" value=\"bold\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M266-192v-576h227.95q67.05 0 123.55 41.32Q674-685.35 674-612q0 51-22.5 79.5T609-490.96Q635-479 665-448t30 91q0 91-67.03 128t-125.81 37H266Zm127-118h104.68Q546-310 556-334.5t10-35.5q0-11-10.5-35.5T494-430H393v120Zm0-232h93q33 0 48.5-17.5T550-597q0-24-17.11-39t-44.28-15H393v109Z\" />\n </svg>\n </button>\n <button (click)=\"formatText('italic')\" ngToolbarWidget type=\"button\" value=\"italic\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M216-192v-96h160l124-384H336v-96h408v96H596L472-288h152v96H216Z\" />\n </svg>\n </button>\n <button (click)=\"formatText('underline')\" ngToolbarWidget type=\"button\" value=\"underline\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M240-144v-72h480v72H240Zm91.5-203.4Q279-406.8 279-504.86V-816h97.21v317.09q0 52.85 26.43 85.88Q429.07-380 480.03-380q50.97 0 77.39-33.03 26.41-33.03 26.41-85.88V-816H681v311.14q0 98.06-52.5 157.46Q576-288 480-288t-148.5-59.4Z\" />\n </svg>\n </button>\n </div>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Block formats -->\n <div class=\"group\" ngToolbarWidgetGroup>\n <button (click)=\"formatHeading('h1')\" ngToolbarWidget type=\"button\" value=\"h1\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M192-288v-384h72v156h168v-156h72v384h-72v-156H264v156h-72Zm504 0v-312h-96v-72h168v384h-72Z\" />\n </svg>\n </button>\n <button (click)=\"formatHeading('h2')\" ngToolbarWidget type=\"button\" value=\"h2\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M144-288v-384h72v156h168v-156h72v384h-72v-156H216v156h-72Zm384 0v-156q0-29.7 21.15-50.85Q570.3-516 600-516h144v-84H528v-72h216.26Q774-672 795-650.85q21 21.15 21 50.85v84q0 29.7-21.15 50.85Q773.7-444 744-444H600v84h216v72H528Z\" />\n </svg>\n </button>\n <button (click)=\"formatParagraph()\" ngToolbarWidget type=\"button\" value=\"p\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M384-192v-192q-79.68 0-135.84-56.23-56.16-56.22-56.16-136Q192-656 248.16-712q56.16-56 135.84-56h336v72h-96v504h-72v-504h-96v504h-72Z\" />\n </svg>\n </button>\n </div>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Lists -->\n <div class=\"group\" ngToolbarWidgetGroup>\n <button (click)=\"formatList('bullet')\" ngToolbarWidget type=\"button\" value=\"bullet\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M360-240v-72h456v72H360Zm0-204v-72h456v72H360Zm0-204v-72h456v72H360ZM215.79-204Q186-204 165-225.21t-21-51Q144-306 165.21-327t51-21Q246-348 267-326.79t21 51Q288-246 266.79-225t-51 21Zm0-204Q186-408 165-429.21t-21-51Q144-510 165.21-531t51-21Q246-552 267-530.79t21 51Q288-450 266.79-429t-51 21ZM165-633.21q-21-21.21-21-51T165.21-735q21.21-21 51-21T267-734.79q21 21.21 21 51T266.79-633q-21.21 21-51 21T165-633.21Z\" />\n </svg>\n </button>\n <button (click)=\"formatList('number')\" ngToolbarWidget type=\"button\" value=\"number\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M144-144v-48h96v-24h-48v-48h48v-24h-96v-48h120q10.2 0 17.1 6.9 6.9 6.9 6.9 17.1v48q0 10.2-6.9 17.1-6.9 6.9-17.1 6.9 10.2 0 17.1 6.9 6.9 6.9 6.9 17.1v48q0 10.2-6.9 17.1-6.9 6.9-17.1 6.9H144Zm0-240v-96q0-10.2 6.9-17.1 6.9-6.9 17.1-6.9h72v-24h-96v-48h120q10.2 0 17.1 6.9 6.9 6.9 6.9 17.1v72q0 10.2-6.9 17.1-6.9 6.9-17.1 6.9h-72v24h96v48H144Zm48-240v-144h-48v-48h96v192h-48Zm168 384v-72h456v72H360Zm0-204v-72h456v72H360Zm0-204v-72h456v72H360Z\" />\n </svg>\n </button>\n <button (click)=\"formatList('check')\" ngToolbarWidget type=\"button\" value=\"check\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M232-216 96-352l51-51 84 85 170-170 52 51-221 221Zm0-312L96-664l51-51 85 85 169-170 52 51-221 221Zm296 240v-72h336v72H528Zm0-312v-72h336v72H528Z\" />\n </svg>\n </button>\n </div>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Code Block -->\n <button (click)=\"formatCodeBlock()\" ngToolbarWidget type=\"button\" value=\"code\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M336-240 96-480l240-240 51 51-189 189 189 189-51 51Zm288 0-51-51 189-189-189-189 51-51 240 240-240 240Z\" />\n </svg>\n </button>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Table (Basic insertion) -->\n <button (click)=\"insertTable()\" ngToolbarWidget type=\"button\" value=\"table\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M216-144q-33 0-52.5-19.5T144-216v-528q0-33 19.5-52.5T216-816h528q33 0 52.5 19.5T816-744v528q0 33-19.5 52.5T744-144H216Zm228-228H216v156h228v-156Zm72 0v156h228v-156H516Zm-72-72v-156H216v156h228Zm72 0h228v-156H516v156ZM216-672h528v-72H216v72Z\" />\n </svg>\n </button>\n </div>\n</div>\n<div class=\"lexical-editor-container\">\n <div #editorContent class=\"lexical-content-editable\" contenteditable=\"true\"></div>\n</div>\n", styles: [":host{height:100%;width:100%;--primary-contrast: #333333;--quinary-contrast: #e0e0e0;--septenary-contrast: #f8f9fa;--hot-pink: #ff69b4}[ngToolbar]{margin-bottom:8px;gap:1.5rem;display:flex;padding:.5rem 1rem;border-radius:.5rem;background-color:var(--septenary-contrast)}.group{gap:.5rem;display:flex}.separator{width:1px;align-self:center;height:calc(100% - 1rem);background-color:var(--quinary-contrast)}[ngToolbarWidget]{border:none;cursor:pointer;padding:.5rem;border-radius:4px;display:flex;align-items:center;justify-content:center;background-color:transparent;color:var(--primary-contrast)}[ngToolbarWidget] svg{fill:currentColor}[ngToolbarWidget]:hover{background-color:color-mix(in srgb,var(--primary-contrast) 10%,transparent)}[ngToolbarWidget]:active{background-color:color-mix(in srgb,var(--primary-contrast) 15%,transparent)}[ngToolbarWidget]:focus{outline-offset:-1px;outline:1px solid color-mix(in srgb,var(--hot-pink) 60%,transparent)}[ngToolbarWidget][aria-pressed=true],[ngToolbarWidget][aria-checked=true]{color:color-mix(in srgb,var(--hot-pink) 80%,var(--primary-contrast));background-color:color-mix(in srgb,var(--hot-pink) 10%,transparent)}.divider{color:var(--quinary-contrast);align-self:center}.lexical-editor-container{border:1px solid #ccc;border-radius:4px;position:relative;background-color:#fff}.lexical-content-editable{min-height:300px;padding:16px;outline:none}.lexical-paragraph{margin:0 0 10px}.lexical-bold{font-weight:700}.lexical-italic{font-style:italic}.lexical-underline{text-decoration:underline}.lexical-strikethrough{text-decoration:line-through}.lexical-inline-code{font-family:monospace;background-color:#f0f0f0;padding:2px 4px;border-radius:2px}.lexical-h1{font-size:2em;margin-top:20px;margin-bottom:10px}.lexical-h2{font-size:1.5em;margin-top:15px;margin-bottom:10px}.lexical-h3{font-size:1.17em;margin-top:10px;margin-bottom:10px}.lexical-ul{padding-left:30px;list-style-type:disc}.lexical-ol{padding-left:30px;list-style-type:decimal}.lexical-listitem{margin-bottom:4px}.lexical-code-block{background-color:#282c34;color:#abb2bf;font-family:Fira Code,monospace;display:block;padding:16px;line-height:1.5;font-size:14px;margin:10px 0;overflow-x:auto;border-radius:6px;tab-size:2}.lexical-table{border-collapse:collapse;border-spacing:0;max-width:100%;overflow-y:scroll;table-layout:fixed;width:100%;margin:10px 0}.lexical-table-row{border-bottom:1px solid #ddd}.lexical-table-cell{border:1px solid #ddd;padding:8px;vertical-align:top;text-align:left}\n"] }]
|
|
191
|
+
}], ctorParameters: () => [], propDecorators: { jsonDataChanged: [{ type: i0.Output, args: ["jsonDataChanged"] }], htmlContentChanged: [{ type: i0.Output, args: ["htmlContentChanged"] }], editorContent: [{
|
|
150
192
|
type: ViewChild,
|
|
151
193
|
args: ['editorContent']
|
|
152
194
|
}] } });
|
|
153
195
|
|
|
154
|
-
class
|
|
196
|
+
class LexicalPreviewComponent {
|
|
155
197
|
debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
|
|
198
|
+
/**
|
|
199
|
+
* Accepts JSON string or SerializedEditorState object directly.
|
|
200
|
+
*/
|
|
201
|
+
data = input(null, ...(ngDevMode ? [{ debugName: "data" }] : /* istanbul ignore next */ []));
|
|
156
202
|
editorService = inject(EditorService);
|
|
157
203
|
sanitizer = inject(DomSanitizer);
|
|
158
|
-
|
|
204
|
+
// If we don't have input data, fallback to the live editor state.
|
|
205
|
+
liveEditorState = this.editorService.getEditorState();
|
|
159
206
|
renderedContent = computed(() => {
|
|
160
|
-
const
|
|
207
|
+
const inputData = this.data();
|
|
208
|
+
// 1. If we have explicit input data, parse it and render
|
|
209
|
+
if (inputData) {
|
|
210
|
+
// We need a temporary headless editor to parse the state and convert to HTML.
|
|
211
|
+
// This is necessary because Lexical's HTML generation requires an editor instance.
|
|
212
|
+
// Using the service's editor config is a good idea to ensure custom nodes match.
|
|
213
|
+
const tempEditor = createEditor({
|
|
214
|
+
// We reuse the nodes registered in the main editor to ensure it knows how to parse them
|
|
215
|
+
nodes: this.editorService.getEditor()?._nodes
|
|
216
|
+
? Array.from(this.editorService.getEditor()._nodes.values()).map((n) => n.klass)
|
|
217
|
+
: [],
|
|
218
|
+
});
|
|
219
|
+
try {
|
|
220
|
+
const editorStateObj = typeof inputData === 'string' ? JSON.parse(inputData) : inputData;
|
|
221
|
+
const state = tempEditor.parseEditorState(editorStateObj);
|
|
222
|
+
tempEditor.setEditorState(state);
|
|
223
|
+
let html = '';
|
|
224
|
+
tempEditor.getEditorState().read(() => {
|
|
225
|
+
html = $generateHtmlFromNodes(tempEditor, null);
|
|
226
|
+
});
|
|
227
|
+
return this.sanitizer.bypassSecurityTrustHtml(html);
|
|
228
|
+
}
|
|
229
|
+
catch (e) {
|
|
230
|
+
console.error('Failed to parse Lexical state in preview:', e);
|
|
231
|
+
return this.sanitizer.bypassSecurityTrustHtml('<div>Sorry an error occurred</div>');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// 2. Fallback to live editor state if no input data provided
|
|
235
|
+
const state = this.liveEditorState();
|
|
161
236
|
if (!state) {
|
|
162
237
|
return this.sanitizer.bypassSecurityTrustHtml('');
|
|
163
238
|
}
|
|
164
239
|
let html = '';
|
|
165
240
|
const editor = this.editorService.getEditor();
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
241
|
+
if (editor) {
|
|
242
|
+
editor.getEditorState().read(() => {
|
|
243
|
+
html = $generateHtmlFromNodes(editor, null);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
170
246
|
return this.sanitizer.bypassSecurityTrustHtml(html);
|
|
171
247
|
}, ...(ngDevMode ? [{ debugName: "renderedContent" }] : /* istanbul ignore next */ []));
|
|
172
248
|
debugJson = computed(() => {
|
|
173
249
|
if (!this.debug()) {
|
|
174
250
|
return null;
|
|
175
251
|
}
|
|
176
|
-
const
|
|
252
|
+
const inputData = this.data();
|
|
253
|
+
if (inputData) {
|
|
254
|
+
return typeof inputData === 'string' ? inputData : JSON.stringify(inputData, null, 2);
|
|
255
|
+
}
|
|
256
|
+
const state = this.liveEditorState();
|
|
177
257
|
return state ? JSON.stringify(state.toJSON(), null, 2) : 'No state available';
|
|
178
258
|
}, ...(ngDevMode ? [{ debugName: "debugJson" }] : /* istanbul ignore next */ []));
|
|
179
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type:
|
|
180
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type:
|
|
259
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: LexicalPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
260
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: LexicalPreviewComponent, isStandalone: true, selector: "lexical-preview", inputs: { debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, data: { classPropertyName: "data", publicName: "data", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div class=\"lexical-preview-container\">\n <!-- Render the visual HTML representation -->\n <div class=\"rendered-content\" [innerHTML]=\"renderedContent()\"></div>\n\n <!-- Render the debug JSON conditionally -->\n @if (debug()) {\n <div class=\"debug-container\">\n <h4>Editor State JSON (Preview)</h4>\n <pre>{{ debugJson() }}</pre>\n </div>\n }\n</div>\n", styles: [".lexical-preview-container{padding:10px}b,strong{font-weight:700}i,em{font-style:italic}u{text-decoration:underline}s{text-decoration:line-through}code{font-family:monospace;background-color:#f0f0f0;padding:2px 4px;border-radius:2px}.debug-container{margin-top:20px;padding:10px;background-color:#f9f9f9;border:1px solid #ddd;border-radius:4px}pre{white-space:pre-wrap;word-wrap:break-word;font-size:12px;margin:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
|
|
181
261
|
}
|
|
182
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type:
|
|
262
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: LexicalPreviewComponent, decorators: [{
|
|
183
263
|
type: Component,
|
|
184
264
|
args: [{ selector: 'lexical-preview', standalone: true, imports: [CommonModule], template: "<div class=\"lexical-preview-container\">\n <!-- Render the visual HTML representation -->\n <div class=\"rendered-content\" [innerHTML]=\"renderedContent()\"></div>\n\n <!-- Render the debug JSON conditionally -->\n @if (debug()) {\n <div class=\"debug-container\">\n <h4>Editor State JSON (Preview)</h4>\n <pre>{{ debugJson() }}</pre>\n </div>\n }\n</div>\n", styles: [".lexical-preview-container{padding:10px}b,strong{font-weight:700}i,em{font-style:italic}u{text-decoration:underline}s{text-decoration:line-through}code{font-family:monospace;background-color:#f0f0f0;padding:2px 4px;border-radius:2px}.debug-container{margin-top:20px;padding:10px;background-color:#f9f9f9;border:1px solid #ddd;border-radius:4px}pre{white-space:pre-wrap;word-wrap:break-word;font-size:12px;margin:0}\n"] }]
|
|
185
|
-
}], propDecorators: { debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }] } });
|
|
265
|
+
}], propDecorators: { debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], data: [{ type: i0.Input, args: [{ isSignal: true, alias: "data", required: false }] }] } });
|
|
186
266
|
|
|
187
267
|
/*
|
|
188
268
|
* Public API Surface of ngx-lexical-editor
|
|
@@ -192,5 +272,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImpo
|
|
|
192
272
|
* Generated bundle index. Do not edit.
|
|
193
273
|
*/
|
|
194
274
|
|
|
195
|
-
export {
|
|
275
|
+
export { LexicalEditorComponent, LexicalPreviewComponent };
|
|
196
276
|
//# sourceMappingURL=ngx-lexical-editor.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngx-lexical-editor.mjs","sources":["../../../projects/ngx-lexical-editor/src/lib/services/editor.service.ts","../../../projects/ngx-lexical-editor/src/lib/components/editor/editor.component.ts","../../../projects/ngx-lexical-editor/src/lib/components/editor/editor.component.html","../../../projects/ngx-lexical-editor/src/lib/components/preview/preview.component.ts","../../../projects/ngx-lexical-editor/src/lib/components/preview/preview.component.html","../../../projects/ngx-lexical-editor/src/public-api.ts","../../../projects/ngx-lexical-editor/src/ngx-lexical-editor.ts"],"sourcesContent":["import { inject, Injectable, PLATFORM_ID, signal } from '@angular/core';\nimport {\n createEditor,\n CreateEditorArgs,\n EditorState,\n LexicalEditor,\n ParagraphNode,\n TextNode,\n} from 'lexical';\nimport { HeadingNode, QuoteNode } from '@lexical/rich-text';\nimport { ListItemNode, ListNode } from '@lexical/list';\nimport { CodeHighlightNode, CodeNode } from '@lexical/code';\nimport { TableCellNode, TableNode, TableRowNode } from '@lexical/table';\nimport { isPlatformBrowser } from '@angular/common';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class EditorService {\n private editor?: LexicalEditor;\n private platformId = inject(PLATFORM_ID);\n private readonly editorState = signal<EditorState | null>(null);\n\n constructor() {\n const editorConfig: CreateEditorArgs = {\n namespace: 'NgxLexicalEditor',\n theme: {\n // Your existing theme...\n paragraph: 'lexical-paragraph',\n heading: {\n h1: 'lexical-h1',\n h2: 'lexical-h2',\n h3: 'lexical-h3',\n },\n list: {\n ul: 'lexical-ul',\n ol: 'lexical-ol',\n listitem: 'lexical-listitem',\n },\n code: 'lexical-code-block',\n table: 'lexical-table',\n tableCell: 'lexical-table-cell',\n tableRow: 'lexical-table-row',\n text: {\n bold: 'lexical-bold',\n italic: 'lexical-italic',\n underline: 'lexical-underline',\n strikethrough: 'lexical-strikethrough',\n code: 'lexical-inline-code',\n },\n },\n nodes: [\n HeadingNode,\n QuoteNode,\n ListNode,\n ListItemNode,\n CodeNode,\n CodeHighlightNode,\n TableNode,\n TableRowNode,\n TableCellNode,\n ParagraphNode, // Ensure ParagraphNode is here if not default\n TextNode, // Ensure TextNode is here if not default\n ],\n onError: (error: Error) => {\n console.error('Lexical Error:', error);\n },\n editable: true,\n };\n\n if (isPlatformBrowser(this.platformId)) {\n this.initEditor(editorConfig);\n }\n }\n\n getEditor(): LexicalEditor | undefined {\n return this.editor;\n }\n\n getEditorState() {\n return this.editorState.asReadonly();\n }\n\n private async initEditor(editorConfig: CreateEditorArgs) {\n this.editor = createEditor(editorConfig);\n\n this.editor.registerUpdateListener(({ editorState }) => {\n this.editorState.set(editorState);\n });\n }\n}\n","import { AfterViewInit, Component, ElementRef, inject, OnDestroy, ViewChild } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { EditorService } from '../../services/editor.service';\nimport {\n $createParagraphNode,\n $getSelection,\n $isRangeSelection,\n FORMAT_TEXT_COMMAND,\n LexicalEditor,\n} from 'lexical';\nimport { $setBlocksType } from '@lexical/selection';\nimport { $createHeadingNode, HeadingTagType, registerRichText } from '@lexical/rich-text';\nimport { $insertList } from '@lexical/list';\nimport { $createCodeNode } from '@lexical/code';\nimport { INSERT_TABLE_COMMAND } from '@lexical/table';\n\n@Component({\n selector: 'lexical-editor',\n standalone: true,\n imports: [CommonModule],\n templateUrl: './editor.component.html',\n styleUrl: './editor.component.css',\n})\nexport class LexicalEditorComponent implements AfterViewInit, OnDestroy {\n @ViewChild('editorContent') editorContent!: ElementRef<HTMLDivElement>;\n private readonly editorService = inject(EditorService);\n private readonly editor?: LexicalEditor;\n\n constructor() {\n this.editor = this.editorService.getEditor();\n if (this.editor) {\n registerRichText(this.editor);\n }\n }\n\n ngAfterViewInit(): void {\n this.editor?.setRootElement(this.editorContent.nativeElement);\n }\n\n ngOnDestroy(): void {\n this.editor?.setRootElement(null);\n }\n\n formatText(type: 'bold' | 'italic' | 'underline'): void {\n this.editor?.dispatchCommand(FORMAT_TEXT_COMMAND, type);\n }\n\n formatHeading(headingSize: HeadingTagType): void {\n this.editor?.update(() => {\n const selection = $getSelection();\n if ($isRangeSelection(selection)) {\n $setBlocksType(selection, () => $createHeadingNode(headingSize));\n }\n });\n }\n\n formatParagraph(): void {\n this.editor?.update(() => {\n const selection = $getSelection();\n if ($isRangeSelection(selection)) {\n $setBlocksType(selection, () => $createParagraphNode());\n }\n });\n }\n\n formatList(listType: 'bullet' | 'number' | 'check'): void {\n this.editor?.update(() => {\n $insertList(listType);\n });\n }\n\n formatCodeBlock(): void {\n this.editor?.update(() => {\n const selection = $getSelection();\n if ($isRangeSelection(selection)) {\n $setBlocksType(selection, () => $createCodeNode());\n }\n });\n }\n\n insertTable(): void {\n this.editor?.dispatchCommand(INSERT_TABLE_COMMAND, {\n rows: '3',\n columns: '3',\n });\n }\n}\n","<div class=\"toolbar\">\n <!-- Inline formats -->\n <button (click)=\"formatText('bold')\">Bold</button>\n <button (click)=\"formatText('italic')\">Italic</button>\n <button (click)=\"formatText('underline')\">Underline</button>\n\n <span class=\"divider\">|</span>\n\n <!-- Block formats -->\n <button (click)=\"formatHeading('h1')\">H1</button>\n <button (click)=\"formatHeading('h2')\">H2</button>\n <button (click)=\"formatParagraph()\">Paragraph</button>\n\n <span class=\"divider\">|</span>\n\n <!-- Lists -->\n <button (click)=\"formatList('bullet')\">Bullet List</button>\n <button (click)=\"formatList('number')\">Number List</button>\n <button (click)=\"formatList('check')\">Check List</button>\n\n <span class=\"divider\">|</span>\n\n <!-- Code Block -->\n <button (click)=\"formatCodeBlock()\">Code Block</button>\n\n <span class=\"divider\">|</span>\n\n <!-- Table (Basic insertion) -->\n <button (click)=\"insertTable()\">Insert Table</button>\n</div>\n<div class=\"lexical-editor-container\">\n <div #editorContent class=\"lexical-content-editable\" contenteditable=\"true\"></div>\n</div>\n","import { Component, computed, inject, input } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { EditorService } from '../../services/editor.service';\nimport { $generateHtmlFromNodes } from '@lexical/html';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\n\n@Component({\n selector: 'lexical-preview',\n standalone: true,\n imports: [CommonModule],\n templateUrl: './preview.component.html',\n styleUrl: './preview.component.css',\n})\nexport class PreviewComponent {\n debug = input<boolean>(false);\n\n private readonly editorService = inject(EditorService);\n private readonly sanitizer = inject(DomSanitizer);\n\n private readonly editorState = this.editorService.getEditorState();\n\n readonly renderedContent = computed<SafeHtml>(() => {\n const state = this.editorState();\n if (!state) {\n return this.sanitizer.bypassSecurityTrustHtml('');\n }\n\n let html = '';\n const editor = this.editorService.getEditor();\n\n // Use a read-only transaction to generate the HTML\n editor?.getEditorState().read(() => {\n html = $generateHtmlFromNodes(editor, null);\n });\n\n return this.sanitizer.bypassSecurityTrustHtml(html);\n });\n\n readonly debugJson = computed(() => {\n if (!this.debug()) {\n return null;\n }\n const state = this.editorState();\n return state ? JSON.stringify(state.toJSON(), null, 2) : 'No state available';\n });\n}\n","<div class=\"lexical-preview-container\">\n <!-- Render the visual HTML representation -->\n <div class=\"rendered-content\" [innerHTML]=\"renderedContent()\"></div>\n\n <!-- Render the debug JSON conditionally -->\n @if (debug()) {\n <div class=\"debug-container\">\n <h4>Editor State JSON (Preview)</h4>\n <pre>{{ debugJson() }}</pre>\n </div>\n }\n</div>\n","/*\n * Public API Surface of ngx-lexical-editor\n */\n\nexport * from './lib/index';\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":"ngx-lexical-editor.mjs","sources":["../../../projects/ngx-lexical-editor/src/lib/services/editor.service.ts","../../../projects/ngx-lexical-editor/src/lib/components/editor/editor.component.ts","../../../projects/ngx-lexical-editor/src/lib/components/editor/editor.component.html","../../../projects/ngx-lexical-editor/src/lib/components/preview/preview.component.ts","../../../projects/ngx-lexical-editor/src/lib/components/preview/preview.component.html","../../../projects/ngx-lexical-editor/src/public-api.ts","../../../projects/ngx-lexical-editor/src/ngx-lexical-editor.ts"],"sourcesContent":["import { computed, inject, PLATFORM_ID, signal, Signal } from '@angular/core';\nimport {\n createEditor,\n CreateEditorArgs,\n EditorState,\n LexicalEditor,\n ParagraphNode,\n SerializedEditorState,\n TextNode\n} from 'lexical';\nimport { HeadingNode, QuoteNode } from '@lexical/rich-text';\nimport { ListItemNode, ListNode } from '@lexical/list';\nimport { CodeHighlightNode, CodeNode } from '@lexical/code';\nimport { TableCellNode, TableNode, TableRowNode } from '@lexical/table';\nimport { isPlatformBrowser } from '@angular/common';\nimport { $generateHtmlFromNodes } from '@lexical/html';\n\nexport class EditorService {\n private editor?: LexicalEditor;\n private platformId = inject(PLATFORM_ID);\n private readonly editorState = signal<EditorState | null>(null);\n\n constructor() {\n const editorConfig: CreateEditorArgs = {\n namespace: 'NgxLexicalEditor',\n theme: {\n paragraph: 'lexical-paragraph',\n heading: {\n h1: 'lexical-h1',\n h2: 'lexical-h2',\n h3: 'lexical-h3',\n },\n list: {\n ul: 'lexical-ul',\n ol: 'lexical-ol',\n listitem: 'lexical-listitem',\n },\n code: 'lexical-code-block',\n table: 'lexical-table',\n tableCell: 'lexical-table-cell',\n tableRow: 'lexical-table-row',\n text: {\n bold: 'lexical-bold',\n italic: 'lexical-italic',\n underline: 'lexical-underline',\n strikethrough: 'lexical-strikethrough',\n code: 'lexical-inline-code',\n },\n },\n nodes: [\n HeadingNode,\n QuoteNode,\n ListNode,\n ListItemNode,\n CodeNode,\n CodeHighlightNode,\n TableNode,\n TableRowNode,\n TableCellNode,\n ParagraphNode,\n TextNode,\n ],\n onError: (error: Error) => {\n console.error('Lexical Error:', error);\n },\n editable: true,\n };\n\n if (isPlatformBrowser(this.platformId)) {\n this.initEditor(editorConfig);\n }\n }\n\n /**\n * JSON Signal of the editor's content\n */\n public get getJSON(): Signal<SerializedEditorState> {\n return computed(() => {\n const state = this.editorState();\n return state\n ? state.toJSON()\n : ({\n root: {\n children: [],\n direction: null,\n format: '',\n indent: 0,\n type: 'root',\n version: 1,\n },\n } as SerializedEditorState);\n });\n }\n\n /**\n * Returns the editor's content as an HTML string. As a signal\n */\n public get getHTML(): Signal<string> {\n return computed(() => {\n const state = this.editorState();\n if (!state || !this.editor) {\n return '';\n }\n\n let html = '';\n this.editor.getEditorState().read(() => {\n html = $generateHtmlFromNodes(this.editor!);\n });\n return html;\n });\n }\n\n getEditorState(): Signal<EditorState | null> {\n return this.editorState.asReadonly();\n }\n\n getEditor(): LexicalEditor | undefined {\n return this.editor;\n }\n\n private async initEditor(editorConfig: CreateEditorArgs) {\n this.editor = createEditor(editorConfig);\n this.editor.registerUpdateListener(({ editorState }) => {\n this.editorState.set(editorState);\n });\n }\n}\n","import {\n AfterViewInit,\n Component,\n effect,\n ElementRef,\n inject,\n OnDestroy,\n output,\n OutputEmitterRef,\n ViewChild,\n} from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { EditorService } from '../../services/editor.service';\nimport {\n $createParagraphNode,\n $getSelection,\n $isRangeSelection,\n FORMAT_TEXT_COMMAND,\n LexicalEditor,\n SerializedEditorState,\n} from 'lexical';\nimport { $setBlocksType } from '@lexical/selection';\nimport { $createHeadingNode, HeadingTagType, registerRichText } from '@lexical/rich-text';\nimport { $insertList } from '@lexical/list';\nimport { $createCodeNode } from '@lexical/code';\nimport { INSERT_TABLE_COMMAND } from '@lexical/table';\nimport { Toolbar, ToolbarWidget, ToolbarWidgetGroup } from '@angular/aria/toolbar';\n\n@Component({\n selector: 'lexical-editor',\n standalone: true,\n imports: [CommonModule, Toolbar, ToolbarWidgetGroup, ToolbarWidget],\n templateUrl: './editor.component.html',\n styleUrl: './editor.component.css',\n})\nexport class LexicalEditorComponent implements AfterViewInit, OnDestroy {\n public readonly jsonDataChanged: OutputEmitterRef<SerializedEditorState> = output();\n public readonly htmlContentChanged: OutputEmitterRef<string> = output();\n @ViewChild('editorContent') protected editorContent!: ElementRef<HTMLDivElement>;\n private readonly editorService = inject(EditorService);\n private readonly editor?: LexicalEditor;\n\n constructor() {\n this.editor = this.editorService.getEditor();\n if (this.editor) {\n registerRichText(this.editor);\n }\n\n effect(() => {\n const json = this.editorService.getJSON();\n if (json) {\n this.jsonDataChanged.emit(json);\n }\n });\n\n effect(() => {\n const html = this.editorService.getHTML();\n if (html) {\n this.htmlContentChanged.emit(html);\n }\n });\n }\n\n ngAfterViewInit(): void {\n this.editor?.setRootElement(this.editorContent.nativeElement);\n }\n\n ngOnDestroy(): void {\n this.editor?.setRootElement(null);\n }\n\n protected formatText(type: 'bold' | 'italic' | 'underline'): void {\n this.editor?.dispatchCommand(FORMAT_TEXT_COMMAND, type);\n }\n\n protected formatHeading(headingSize: HeadingTagType): void {\n this.editor?.update(() => {\n const selection = $getSelection();\n if ($isRangeSelection(selection)) {\n $setBlocksType(selection, () => $createHeadingNode(headingSize));\n }\n });\n }\n\n protected formatParagraph(): void {\n this.editor?.update(() => {\n const selection = $getSelection();\n if ($isRangeSelection(selection)) {\n $setBlocksType(selection, () => $createParagraphNode());\n }\n });\n }\n\n protected formatList(listType: 'bullet' | 'number' | 'check'): void {\n this.editor?.update(() => {\n $insertList(listType);\n });\n }\n\n protected formatCodeBlock(): void {\n this.editor?.update(() => {\n const selection = $getSelection();\n if ($isRangeSelection(selection)) {\n $setBlocksType(selection, () => $createCodeNode());\n }\n });\n }\n\n protected insertTable(): void {\n this.editor?.dispatchCommand(INSERT_TABLE_COMMAND, {\n rows: '3',\n columns: '3',\n });\n }\n}\n","<div class=\"toolbar\">\n <div aria-label=\"Text Formatting Tools\" class=\"toolbar\" ngToolbar>\n\n <!-- Inline formats -->\n <div class=\"group\" ngToolbarWidgetGroup>\n <button (click)=\"formatText('bold')\" ngToolbarWidget type=\"button\" value=\"bold\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M266-192v-576h227.95q67.05 0 123.55 41.32Q674-685.35 674-612q0 51-22.5 79.5T609-490.96Q635-479 665-448t30 91q0 91-67.03 128t-125.81 37H266Zm127-118h104.68Q546-310 556-334.5t10-35.5q0-11-10.5-35.5T494-430H393v120Zm0-232h93q33 0 48.5-17.5T550-597q0-24-17.11-39t-44.28-15H393v109Z\" />\n </svg>\n </button>\n <button (click)=\"formatText('italic')\" ngToolbarWidget type=\"button\" value=\"italic\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M216-192v-96h160l124-384H336v-96h408v96H596L472-288h152v96H216Z\" />\n </svg>\n </button>\n <button (click)=\"formatText('underline')\" ngToolbarWidget type=\"button\" value=\"underline\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M240-144v-72h480v72H240Zm91.5-203.4Q279-406.8 279-504.86V-816h97.21v317.09q0 52.85 26.43 85.88Q429.07-380 480.03-380q50.97 0 77.39-33.03 26.41-33.03 26.41-85.88V-816H681v311.14q0 98.06-52.5 157.46Q576-288 480-288t-148.5-59.4Z\" />\n </svg>\n </button>\n </div>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Block formats -->\n <div class=\"group\" ngToolbarWidgetGroup>\n <button (click)=\"formatHeading('h1')\" ngToolbarWidget type=\"button\" value=\"h1\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M192-288v-384h72v156h168v-156h72v384h-72v-156H264v156h-72Zm504 0v-312h-96v-72h168v384h-72Z\" />\n </svg>\n </button>\n <button (click)=\"formatHeading('h2')\" ngToolbarWidget type=\"button\" value=\"h2\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M144-288v-384h72v156h168v-156h72v384h-72v-156H216v156h-72Zm384 0v-156q0-29.7 21.15-50.85Q570.3-516 600-516h144v-84H528v-72h216.26Q774-672 795-650.85q21 21.15 21 50.85v84q0 29.7-21.15 50.85Q773.7-444 744-444H600v84h216v72H528Z\" />\n </svg>\n </button>\n <button (click)=\"formatParagraph()\" ngToolbarWidget type=\"button\" value=\"p\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M384-192v-192q-79.68 0-135.84-56.23-56.16-56.22-56.16-136Q192-656 248.16-712q56.16-56 135.84-56h336v72h-96v504h-72v-504h-96v504h-72Z\" />\n </svg>\n </button>\n </div>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Lists -->\n <div class=\"group\" ngToolbarWidgetGroup>\n <button (click)=\"formatList('bullet')\" ngToolbarWidget type=\"button\" value=\"bullet\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M360-240v-72h456v72H360Zm0-204v-72h456v72H360Zm0-204v-72h456v72H360ZM215.79-204Q186-204 165-225.21t-21-51Q144-306 165.21-327t51-21Q246-348 267-326.79t21 51Q288-246 266.79-225t-51 21Zm0-204Q186-408 165-429.21t-21-51Q144-510 165.21-531t51-21Q246-552 267-530.79t21 51Q288-450 266.79-429t-51 21ZM165-633.21q-21-21.21-21-51T165.21-735q21.21-21 51-21T267-734.79q21 21.21 21 51T266.79-633q-21.21 21-51 21T165-633.21Z\" />\n </svg>\n </button>\n <button (click)=\"formatList('number')\" ngToolbarWidget type=\"button\" value=\"number\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M144-144v-48h96v-24h-48v-48h48v-24h-96v-48h120q10.2 0 17.1 6.9 6.9 6.9 6.9 17.1v48q0 10.2-6.9 17.1-6.9 6.9-17.1 6.9 10.2 0 17.1 6.9 6.9 6.9 6.9 17.1v48q0 10.2-6.9 17.1-6.9 6.9-17.1 6.9H144Zm0-240v-96q0-10.2 6.9-17.1 6.9-6.9 17.1-6.9h72v-24h-96v-48h120q10.2 0 17.1 6.9 6.9 6.9 6.9 17.1v72q0 10.2-6.9 17.1-6.9 6.9-17.1 6.9h-72v24h96v48H144Zm48-240v-144h-48v-48h96v192h-48Zm168 384v-72h456v72H360Zm0-204v-72h456v72H360Zm0-204v-72h456v72H360Z\" />\n </svg>\n </button>\n <button (click)=\"formatList('check')\" ngToolbarWidget type=\"button\" value=\"check\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M232-216 96-352l51-51 84 85 170-170 52 51-221 221Zm0-312L96-664l51-51 85 85 169-170 52 51-221 221Zm296 240v-72h336v72H528Zm0-312v-72h336v72H528Z\" />\n </svg>\n </button>\n </div>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Code Block -->\n <button (click)=\"formatCodeBlock()\" ngToolbarWidget type=\"button\" value=\"code\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M336-240 96-480l240-240 51 51-189 189 189 189-51 51Zm288 0-51-51 189-189-189-189 51-51 240 240-240 240Z\" />\n </svg>\n </button>\n\n <span aria-hidden=\"true\" class=\"divider\">|</span>\n\n <!-- Table (Basic insertion) -->\n <button (click)=\"insertTable()\" ngToolbarWidget type=\"button\" value=\"table\">\n <svg fill=\"#808080\" height=\"20px\" viewBox=\"0 -960 960 960\" width=\"20px\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M216-144q-33 0-52.5-19.5T144-216v-528q0-33 19.5-52.5T216-816h528q33 0 52.5 19.5T816-744v528q0 33-19.5 52.5T744-144H216Zm228-228H216v156h228v-156Zm72 0v156h228v-156H516Zm-72-72v-156H216v156h228Zm72 0h228v-156H516v156ZM216-672h528v-72H216v72Z\" />\n </svg>\n </button>\n </div>\n</div>\n<div class=\"lexical-editor-container\">\n <div #editorContent class=\"lexical-content-editable\" contenteditable=\"true\"></div>\n</div>\n","import { Component, computed, inject, input } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { EditorService } from '../../services/editor.service';\nimport { $generateHtmlFromNodes } from '@lexical/html';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { createEditor, SerializedEditorState } from 'lexical';\n\n@Component({\n selector: 'lexical-preview',\n standalone: true,\n imports: [CommonModule],\n templateUrl: './preview.component.html',\n styleUrl: './preview.component.css',\n})\nexport class LexicalPreviewComponent {\n debug = input<boolean>(false);\n\n /**\n * Accepts JSON string or SerializedEditorState object directly.\n */\n data = input<string | SerializedEditorState | null>(null);\n\n private readonly editorService = inject(EditorService);\n private readonly sanitizer = inject(DomSanitizer);\n\n // If we don't have input data, fallback to the live editor state.\n private readonly liveEditorState = this.editorService.getEditorState();\n\n protected readonly renderedContent = computed<SafeHtml>(() => {\n const inputData = this.data();\n\n // 1. If we have explicit input data, parse it and render\n if (inputData) {\n // We need a temporary headless editor to parse the state and convert to HTML.\n // This is necessary because Lexical's HTML generation requires an editor instance.\n // Using the service's editor config is a good idea to ensure custom nodes match.\n const tempEditor = createEditor({\n // We reuse the nodes registered in the main editor to ensure it knows how to parse them\n nodes: this.editorService.getEditor()?._nodes\n ? Array.from(this.editorService.getEditor()!._nodes.values()).map((n) => n.klass)\n : [],\n });\n\n try {\n const editorStateObj = typeof inputData === 'string' ? JSON.parse(inputData) : inputData;\n const state = tempEditor.parseEditorState(editorStateObj);\n tempEditor.setEditorState(state);\n\n let html = '';\n tempEditor.getEditorState().read(() => {\n html = $generateHtmlFromNodes(tempEditor, null);\n });\n return this.sanitizer.bypassSecurityTrustHtml(html);\n } catch (e) {\n console.error('Failed to parse Lexical state in preview:', e);\n return this.sanitizer.bypassSecurityTrustHtml('<div>Sorry an error occurred</div>');\n }\n }\n\n // 2. Fallback to live editor state if no input data provided\n const state = this.liveEditorState();\n if (!state) {\n return this.sanitizer.bypassSecurityTrustHtml('');\n }\n\n let html = '';\n const editor = this.editorService.getEditor();\n\n if (editor) {\n editor.getEditorState().read(() => {\n html = $generateHtmlFromNodes(editor, null);\n });\n }\n\n return this.sanitizer.bypassSecurityTrustHtml(html);\n });\n\n protected readonly debugJson = computed(() => {\n if (!this.debug()) {\n return null;\n }\n\n const inputData = this.data();\n if (inputData) {\n return typeof inputData === 'string' ? inputData : JSON.stringify(inputData, null, 2);\n }\n\n const state = this.liveEditorState();\n return state ? JSON.stringify(state.toJSON(), null, 2) : 'No state available';\n });\n}\n","<div class=\"lexical-preview-container\">\n <!-- Render the visual HTML representation -->\n <div class=\"rendered-content\" [innerHTML]=\"renderedContent()\"></div>\n\n <!-- Render the debug JSON conditionally -->\n @if (debug()) {\n <div class=\"debug-container\">\n <h4>Editor State JSON (Preview)</h4>\n <pre>{{ debugJson() }}</pre>\n </div>\n }\n</div>\n","/*\n * Public API Surface of ngx-lexical-editor\n */\n\nexport * from './lib/index';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,47 +1,42 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { AfterViewInit, OnDestroy, ElementRef } from '@angular/core';
|
|
1
|
+
import * as _angular_core from '@angular/core';
|
|
2
|
+
import { AfterViewInit, OnDestroy, OutputEmitterRef, ElementRef } from '@angular/core';
|
|
3
|
+
import * as lexical from 'lexical';
|
|
4
|
+
import { SerializedEditorState } from 'lexical';
|
|
3
5
|
import { HeadingTagType } from '@lexical/rich-text';
|
|
4
6
|
import { SafeHtml } from '@angular/platform-browser';
|
|
5
|
-
import { LexicalEditor, EditorState } from 'lexical';
|
|
6
7
|
|
|
7
8
|
declare class LexicalEditorComponent implements AfterViewInit, OnDestroy {
|
|
8
|
-
|
|
9
|
+
readonly jsonDataChanged: OutputEmitterRef<SerializedEditorState>;
|
|
10
|
+
readonly htmlContentChanged: OutputEmitterRef<string>;
|
|
11
|
+
protected editorContent: ElementRef<HTMLDivElement>;
|
|
9
12
|
private readonly editorService;
|
|
10
13
|
private readonly editor?;
|
|
11
14
|
constructor();
|
|
12
15
|
ngAfterViewInit(): void;
|
|
13
16
|
ngOnDestroy(): void;
|
|
14
|
-
formatText(type: 'bold' | 'italic' | 'underline'): void;
|
|
15
|
-
formatHeading(headingSize: HeadingTagType): void;
|
|
16
|
-
formatParagraph(): void;
|
|
17
|
-
formatList(listType: 'bullet' | 'number' | 'check'): void;
|
|
18
|
-
formatCodeBlock(): void;
|
|
19
|
-
insertTable(): void;
|
|
20
|
-
static ɵfac:
|
|
21
|
-
static ɵcmp:
|
|
17
|
+
protected formatText(type: 'bold' | 'italic' | 'underline'): void;
|
|
18
|
+
protected formatHeading(headingSize: HeadingTagType): void;
|
|
19
|
+
protected formatParagraph(): void;
|
|
20
|
+
protected formatList(listType: 'bullet' | 'number' | 'check'): void;
|
|
21
|
+
protected formatCodeBlock(): void;
|
|
22
|
+
protected insertTable(): void;
|
|
23
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<LexicalEditorComponent, never>;
|
|
24
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<LexicalEditorComponent, "lexical-editor", never, {}, { "jsonDataChanged": "jsonDataChanged"; "htmlContentChanged": "htmlContentChanged"; }, never, never, true, never>;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
declare class
|
|
25
|
-
debug:
|
|
27
|
+
declare class LexicalPreviewComponent {
|
|
28
|
+
debug: _angular_core.InputSignal<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Accepts JSON string or SerializedEditorState object directly.
|
|
31
|
+
*/
|
|
32
|
+
data: _angular_core.InputSignal<string | SerializedEditorState<lexical.SerializedLexicalNode> | null>;
|
|
26
33
|
private readonly editorService;
|
|
27
34
|
private readonly sanitizer;
|
|
28
|
-
private readonly
|
|
29
|
-
readonly renderedContent:
|
|
30
|
-
readonly debugJson:
|
|
31
|
-
static ɵfac:
|
|
32
|
-
static ɵcmp:
|
|
35
|
+
private readonly liveEditorState;
|
|
36
|
+
protected readonly renderedContent: _angular_core.Signal<SafeHtml>;
|
|
37
|
+
protected readonly debugJson: _angular_core.Signal<string | null>;
|
|
38
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<LexicalPreviewComponent, never>;
|
|
39
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<LexicalPreviewComponent, "lexical-preview", never, { "debug": { "alias": "debug"; "required": false; "isSignal": true; }; "data": { "alias": "data"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
33
40
|
}
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
private editor?;
|
|
37
|
-
private platformId;
|
|
38
|
-
private readonly editorState;
|
|
39
|
-
constructor();
|
|
40
|
-
getEditor(): LexicalEditor | undefined;
|
|
41
|
-
getEditorState(): i0.Signal<EditorState | null>;
|
|
42
|
-
private initEditor;
|
|
43
|
-
static ɵfac: i0.ɵɵFactoryDeclaration<EditorService, never>;
|
|
44
|
-
static ɵprov: i0.ɵɵInjectableDeclaration<EditorService>;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export { EditorService, LexicalEditorComponent, PreviewComponent };
|
|
42
|
+
export { LexicalEditorComponent, LexicalPreviewComponent };
|