ngx-lexical-editor 1.0.0 → 1.1.1

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 CHANGED
@@ -1,64 +1,101 @@
1
- # NgxLexicalEditor
1
+ # ngx-lexical-editor
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.0.
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
- ## Code scaffolding
5
+ ## Features
6
6
 
7
- Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
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
- ```bash
10
- ng generate component component-name
11
- ```
14
+ ## Installation
12
15
 
13
- For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
16
+ Install the library and its peer dependencies using your preferred package manager (npm, yarn, or pnpm):
14
17
 
15
18
  ```bash
16
- ng generate --help
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
- ## Building
20
-
21
- To build the library, run:
22
-
23
- ```bash
24
- ng build ngx-lexical-editor
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
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
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
- ```bash
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
- 2. Run the `npm publish` command to publish your library to the npm registry:
40
- ```bash
41
- npm publish
42
- ```
84
+ ### Setup for Local Development
43
85
 
44
- ## Running unit tests
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
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
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
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
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
- ## Additional Resources
99
+ ## License
63
100
 
64
- For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
101
+ MIT License
@@ -1,13 +1,14 @@
1
1
  import * as i0 from '@angular/core';
2
- import { inject, PLATFORM_ID, signal, Injectable, ViewChild, Component, input, computed } from '@angular/core';
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, // Ensure ParagraphNode is here if not default
56
- TextNode, // Ensure TextNode is here if not default
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
- getEditor() {
68
- return this.editor;
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 <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", styles: [".toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:4px;margin-bottom:8px;padding:8px;background-color:#f5f5f5;border-radius:4px;border:1px solid #ddd}.toolbar button{padding:4px 8px;cursor:pointer;background-color:#fff;border:1px solid #ccc;border-radius:3px;font-size:14px}.toolbar button:hover{background-color:#e9e9e9}.divider{color:#ccc;margin:0 4px}.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 }] });
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" }, providers: [EditorService], 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 <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", styles: [".toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:4px;margin-bottom:8px;padding:8px;background-color:#f5f5f5;border-radius:4px;border:1px solid #ddd}.toolbar button{padding:4px 8px;cursor:pointer;background-color:#fff;border:1px solid #ccc;border-radius:3px;font-size:14px}.toolbar button:hover{background-color:#e9e9e9}.divider{color:#ccc;margin:0 4px}.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"] }]
149
- }], ctorParameters: () => [], propDecorators: { editorContent: [{
190
+ args: [{ selector: 'lexical-editor', standalone: true, imports: [CommonModule, Toolbar, ToolbarWidgetGroup, ToolbarWidget], providers: [EditorService], 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 PreviewComponent {
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
- editorState = this.editorService.getEditorState();
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 state = this.editorState();
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
- // Use a read-only transaction to generate the HTML
167
- editor?.getEditorState().read(() => {
168
- html = $generateHtmlFromNodes(editor, null);
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 state = this.editorState();
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: PreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
180
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: PreviewComponent, isStandalone: true, selector: "lexical-preview", inputs: { debug: { classPropertyName: "debug", publicName: "debug", 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 }] });
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 } }, providers: [EditorService], 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: PreviewComponent, decorators: [{
262
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: LexicalPreviewComponent, decorators: [{
183
263
  type: Component,
184
- 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 }] }] } });
264
+ args: [{ selector: 'lexical-preview', standalone: true, imports: [CommonModule], providers: [EditorService], 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"] }]
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 { EditorService, LexicalEditorComponent, PreviewComponent };
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 providers: [EditorService],\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 providers: [EditorService],\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":";;;;;;;;;;;;;MAiBa,aAAa,CAAA;AAChB,IAAA,MAAM;AACN,IAAA,UAAU,GAAG,MAAM,CAAC,WAAW,CAAC;AACvB,IAAA,WAAW,GAAG,MAAM,CAAqB,IAAI,kFAAC;AAE/D,IAAA,WAAA,GAAA;AACE,QAAA,MAAM,YAAY,GAAqB;AACrC,YAAA,SAAS,EAAE,kBAAkB;AAC7B,YAAA,KAAK,EAAE;AACL,gBAAA,SAAS,EAAE,mBAAmB;AAC9B,gBAAA,OAAO,EAAE;AACP,oBAAA,EAAE,EAAE,YAAY;AAChB,oBAAA,EAAE,EAAE,YAAY;AAChB,oBAAA,EAAE,EAAE,YAAY;AACjB,iBAAA;AACD,gBAAA,IAAI,EAAE;AACJ,oBAAA,EAAE,EAAE,YAAY;AAChB,oBAAA,EAAE,EAAE,YAAY;AAChB,oBAAA,QAAQ,EAAE,kBAAkB;AAC7B,iBAAA;AACD,gBAAA,IAAI,EAAE,oBAAoB;AAC1B,gBAAA,KAAK,EAAE,eAAe;AACtB,gBAAA,SAAS,EAAE,oBAAoB;AAC/B,gBAAA,QAAQ,EAAE,mBAAmB;AAC7B,gBAAA,IAAI,EAAE;AACJ,oBAAA,IAAI,EAAE,cAAc;AACpB,oBAAA,MAAM,EAAE,gBAAgB;AACxB,oBAAA,SAAS,EAAE,mBAAmB;AAC9B,oBAAA,aAAa,EAAE,uBAAuB;AACtC,oBAAA,IAAI,EAAE,qBAAqB;AAC5B,iBAAA;AACF,aAAA;AACD,YAAA,KAAK,EAAE;gBACL,WAAW;gBACX,SAAS;gBACT,QAAQ;gBACR,YAAY;gBACZ,QAAQ;gBACR,iBAAiB;gBACjB,SAAS;gBACT,YAAY;gBACZ,aAAa;gBACb,aAAa;gBACb,QAAQ;AACT,aAAA;AACD,YAAA,OAAO,EAAE,CAAC,KAAY,KAAI;AACxB,gBAAA,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC;YACxC,CAAC;AACD,YAAA,QAAQ,EAAE,IAAI;SACf;AAED,QAAA,IAAI,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE;AACtC,YAAA,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC;QAC/B;IACF;AAEA;;AAEG;AACH,IAAA,IAAW,OAAO,GAAA;QAChB,OAAO,QAAQ,CAAC,MAAK;AACnB,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE;AAChC,YAAA,OAAO;AACL,kBAAE,KAAK,CAAC,MAAM;AACd,kBAAG;AACC,oBAAA,IAAI,EAAE;AACJ,wBAAA,QAAQ,EAAE,EAAE;AACZ,wBAAA,SAAS,EAAE,IAAI;AACf,wBAAA,MAAM,EAAE,EAAE;AACV,wBAAA,MAAM,EAAE,CAAC;AACT,wBAAA,IAAI,EAAE,MAAM;AACZ,wBAAA,OAAO,EAAE,CAAC;AACX,qBAAA;iBACwB;AACjC,QAAA,CAAC,CAAC;IACJ;AAEA;;AAEG;AACH,IAAA,IAAW,OAAO,GAAA;QAChB,OAAO,QAAQ,CAAC,MAAK;AACnB,YAAA,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE;YAChC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE;AAC1B,gBAAA,OAAO,EAAE;YACX;YAEA,IAAI,IAAI,GAAG,EAAE;YACb,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,MAAK;AACrC,gBAAA,IAAI,GAAG,sBAAsB,CAAC,IAAI,CAAC,MAAO,CAAC;AAC7C,YAAA,CAAC,CAAC;AACF,YAAA,OAAO,IAAI;AACb,QAAA,CAAC,CAAC;IACJ;IAEA,cAAc,GAAA;AACZ,QAAA,OAAO,IAAI,CAAC,WAAW,CAAC,UAAU,EAAE;IACtC;IAEA,SAAS,GAAA;QACP,OAAO,IAAI,CAAC,MAAM;IACpB;IAEQ,MAAM,UAAU,CAAC,YAA8B,EAAA;AACrD,QAAA,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC,YAAY,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,EAAE,WAAW,EAAE,KAAI;AACrD,YAAA,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC;AACnC,QAAA,CAAC,CAAC;IACJ;AACD;;MC1FY,sBAAsB,CAAA;IACjB,eAAe,GAA4C,MAAM,EAAE;IACnE,kBAAkB,GAA6B,MAAM,EAAE;AACjC,IAAA,aAAa;AAClC,IAAA,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;AACrC,IAAA,MAAM;AAEvB,IAAA,WAAA,GAAA;QACE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE;AAC5C,QAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AACf,YAAA,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC;QAC/B;QAEA,MAAM,CAAC,MAAK;YACV,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE;YACzC,IAAI,IAAI,EAAE;AACR,gBAAA,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC;YACjC;AACF,QAAA,CAAC,CAAC;QAEF,MAAM,CAAC,MAAK;YACV,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,OAAO,EAAE;YACzC,IAAI,IAAI,EAAE;AACR,gBAAA,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;YACpC;AACF,QAAA,CAAC,CAAC;IACJ;IAEA,eAAe,GAAA;QACb,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC;IAC/D;IAEA,WAAW,GAAA;AACT,QAAA,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,CAAC;IACnC;AAEU,IAAA,UAAU,CAAC,IAAqC,EAAA;QACxD,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,mBAAmB,EAAE,IAAI,CAAC;IACzD;AAEU,IAAA,aAAa,CAAC,WAA2B,EAAA;AACjD,QAAA,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAK;AACvB,YAAA,MAAM,SAAS,GAAG,aAAa,EAAE;AACjC,YAAA,IAAI,iBAAiB,CAAC,SAAS,CAAC,EAAE;gBAChC,cAAc,CAAC,SAAS,EAAE,MAAM,kBAAkB,CAAC,WAAW,CAAC,CAAC;YAClE;AACF,QAAA,CAAC,CAAC;IACJ;IAEU,eAAe,GAAA;AACvB,QAAA,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAK;AACvB,YAAA,MAAM,SAAS,GAAG,aAAa,EAAE;AACjC,YAAA,IAAI,iBAAiB,CAAC,SAAS,CAAC,EAAE;gBAChC,cAAc,CAAC,SAAS,EAAE,MAAM,oBAAoB,EAAE,CAAC;YACzD;AACF,QAAA,CAAC,CAAC;IACJ;AAEU,IAAA,UAAU,CAAC,QAAuC,EAAA;AAC1D,QAAA,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAK;YACvB,WAAW,CAAC,QAAQ,CAAC;AACvB,QAAA,CAAC,CAAC;IACJ;IAEU,eAAe,GAAA;AACvB,QAAA,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,MAAK;AACvB,YAAA,MAAM,SAAS,GAAG,aAAa,EAAE;AACjC,YAAA,IAAI,iBAAiB,CAAC,SAAS,CAAC,EAAE;gBAChC,cAAc,CAAC,SAAS,EAAE,MAAM,eAAe,EAAE,CAAC;YACpD;AACF,QAAA,CAAC,CAAC;IACJ;IAEU,WAAW,GAAA;AACnB,QAAA,IAAI,CAAC,MAAM,EAAE,eAAe,CAAC,oBAAoB,EAAE;AACjD,YAAA,IAAI,EAAE,GAAG;AACT,YAAA,OAAO,EAAE,GAAG;AACb,SAAA,CAAC;IACJ;wGA9EW,sBAAsB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAtB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,sBAAsB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,gBAAA,EAAA,OAAA,EAAA,EAAA,eAAA,EAAA,iBAAA,EAAA,kBAAA,EAAA,oBAAA,EAAA,EAAA,SAAA,EAFtB,CAAC,aAAa,CAAC,EAAA,WAAA,EAAA,CAAA,EAAA,YAAA,EAAA,eAAA,EAAA,KAAA,EAAA,IAAA,EAAA,SAAA,EAAA,CAAA,eAAA,CAAA,EAAA,WAAA,EAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EClC5B,wuMA+FA,EAAA,MAAA,EAAA,CAAA,w8EAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EDhEY,YAAY,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,OAAO,EAAA,QAAA,EAAA,aAAA,EAAA,MAAA,EAAA,CAAA,aAAA,EAAA,cAAA,EAAA,UAAA,EAAA,MAAA,EAAA,QAAA,CAAA,EAAA,OAAA,EAAA,CAAA,cAAA,CAAA,EAAA,QAAA,EAAA,CAAA,WAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,kBAAkB,sIAAE,aAAa,EAAA,QAAA,EAAA,mBAAA,EAAA,MAAA,EAAA,CAAA,IAAA,EAAA,UAAA,EAAA,OAAA,CAAA,EAAA,QAAA,EAAA,CAAA,iBAAA,CAAA,EAAA,CAAA,EAAA,CAAA;;4FAKvD,sBAAsB,EAAA,UAAA,EAAA,CAAA;kBARlC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,gBAAgB,EAAA,UAAA,EACd,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,aAAa,CAAC,EAAA,SAAA,EAGxD,CAAC,aAAa,CAAC,EAAA,QAAA,EAAA,wuMAAA,EAAA,MAAA,EAAA,CAAA,w8EAAA,CAAA,EAAA;;sBAKzB,SAAS;uBAAC,eAAe;;;MExBf,uBAAuB,CAAA;AAClC,IAAA,KAAK,GAAG,KAAK,CAAU,KAAK,4EAAC;AAE7B;;AAEG;AACH,IAAA,IAAI,GAAG,KAAK,CAAwC,IAAI,2EAAC;AAExC,IAAA,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;AACrC,IAAA,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC;;AAGhC,IAAA,eAAe,GAAG,IAAI,CAAC,aAAa,CAAC,cAAc,EAAE;AAEnD,IAAA,eAAe,GAAG,QAAQ,CAAW,MAAK;AAC3D,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE;;QAG7B,IAAI,SAAS,EAAE;;;;YAIb,MAAM,UAAU,GAAG,YAAY,CAAC;;gBAE9B,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE,EAAE;AACrC,sBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,SAAS,EAAG,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK;AAChF,sBAAE,EAAE;AACP,aAAA,CAAC;AAEF,YAAA,IAAI;AACF,gBAAA,MAAM,cAAc,GAAG,OAAO,SAAS,KAAK,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,SAAS;gBACxF,MAAM,KAAK,GAAG,UAAU,CAAC,gBAAgB,CAAC,cAAc,CAAC;AACzD,gBAAA,UAAU,CAAC,cAAc,CAAC,KAAK,CAAC;gBAEhC,IAAI,IAAI,GAAG,EAAE;AACb,gBAAA,UAAU,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,MAAK;AACpC,oBAAA,IAAI,GAAG,sBAAsB,CAAC,UAAU,EAAE,IAAI,CAAC;AACjD,gBAAA,CAAC,CAAC;gBACF,OAAO,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,IAAI,CAAC;YACrD;YAAE,OAAO,CAAC,EAAE;AACV,gBAAA,OAAO,CAAC,KAAK,CAAC,2CAA2C,EAAE,CAAC,CAAC;gBAC7D,OAAO,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,oCAAoC,CAAC;YACrF;QACF;;AAGA,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,EAAE;QACpC,IAAI,CAAC,KAAK,EAAE;YACV,OAAO,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,EAAE,CAAC;QACnD;QAEA,IAAI,IAAI,GAAG,EAAE;QACb,MAAM,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,SAAS,EAAE;QAE7C,IAAI,MAAM,EAAE;AACV,YAAA,MAAM,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,MAAK;AAChC,gBAAA,IAAI,GAAG,sBAAsB,CAAC,MAAM,EAAE,IAAI,CAAC;AAC7C,YAAA,CAAC,CAAC;QACJ;QAEA,OAAO,IAAI,CAAC,SAAS,CAAC,uBAAuB,CAAC,IAAI,CAAC;AACrD,IAAA,CAAC,sFAAC;AAEiB,IAAA,SAAS,GAAG,QAAQ,CAAC,MAAK;AAC3C,QAAA,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE;AACjB,YAAA,OAAO,IAAI;QACb;AAEA,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,EAAE;QAC7B,IAAI,SAAS,EAAE;YACb,OAAO,OAAO,SAAS,KAAK,QAAQ,GAAG,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvF;AAEA,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,EAAE;QACpC,OAAO,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,oBAAoB;AAC/E,IAAA,CAAC,gFAAC;wGA3ES,uBAAuB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAvB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,SAAA,EAAA,IAAA,EAAA,uBAAuB,uTAFvB,CAAC,aAAa,CAAC,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,ECb5B,8XAYA,ydDFY,YAAY,EAAA,CAAA,EAAA,CAAA;;4FAKX,uBAAuB,EAAA,UAAA,EAAA,CAAA;kBARnC,SAAS;+BACE,iBAAiB,EAAA,UAAA,EACf,IAAI,EAAA,OAAA,EACP,CAAC,YAAY,CAAC,EAAA,SAAA,EAGZ,CAAC,aAAa,CAAC,EAAA,QAAA,EAAA,8XAAA,EAAA,MAAA,EAAA,CAAA,kaAAA,CAAA,EAAA;;;AEb5B;;AAEG;;ACFH;;AAEG;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ngx-lexical-editor",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "peerDependencies": {
5
5
  "@angular/common": "^21.2.0",
6
6
  "@angular/core": "^21.2.0",
@@ -1,47 +1,42 @@
1
- import * as i0 from '@angular/core';
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
- editorContent: ElementRef<HTMLDivElement>;
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: i0.ɵɵFactoryDeclaration<LexicalEditorComponent, never>;
21
- static ɵcmp: i0.ɵɵComponentDeclaration<LexicalEditorComponent, "lexical-editor", never, {}, {}, never, never, true, never>;
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 PreviewComponent {
25
- debug: i0.InputSignal<boolean>;
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 editorState;
29
- readonly renderedContent: i0.Signal<SafeHtml>;
30
- readonly debugJson: i0.Signal<string | null>;
31
- static ɵfac: i0.ɵɵFactoryDeclaration<PreviewComponent, never>;
32
- static ɵcmp: i0.ɵɵComponentDeclaration<PreviewComponent, "lexical-preview", never, { "debug": { "alias": "debug"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
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
- declare class EditorService {
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 };