ngx-rich-text-pro 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1973 @@
1
+ import * as i0 from '@angular/core';
2
+ import { Input, Component, EventEmitter, HostListener, Output, Injectable, forwardRef, ViewChild } from '@angular/core';
3
+ import * as i3 from '@angular/common';
4
+ import { CommonModule } from '@angular/common';
5
+ import * as i4 from '@angular/forms';
6
+ import { FormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
7
+ import * as i2 from '@angular/platform-browser';
8
+
9
+ const DEFAULT_FONT_FAMILIES = [
10
+ { label: 'Arial', value: 'Arial, sans-serif' },
11
+ { label: 'Calibri', value: "'Calibri', sans-serif" },
12
+ { label: 'Comic Sans', value: "'Comic Sans MS', cursive" },
13
+ { label: 'Courier New', value: "'Courier New', monospace" },
14
+ { label: 'Georgia', value: "'Georgia', serif" },
15
+ { label: 'Impact', value: "'Impact', sans-serif" },
16
+ { label: 'Inter', value: "'Inter', sans-serif" },
17
+ { label: 'Poppins', value: "'Poppins', sans-serif" },
18
+ { label: 'Roboto', value: "'Roboto', sans-serif" },
19
+ { label: 'Times New Roman', value: "'Times New Roman', serif" },
20
+ { label: 'Trebuchet MS', value: "'Trebuchet MS', sans-serif" },
21
+ { label: 'Verdana', value: "'Verdana', sans-serif" },
22
+ ];
23
+ const DEFAULT_COLOR_PALETTE = [
24
+ '#000000', '#333333', '#666666', '#999999', '#cccccc', '#ffffff',
25
+ '#e60000', '#ff9900', '#ffff00', '#008a00', '#0066cc', '#9933ff',
26
+ '#ff9999', '#ffcc99', '#ffff99', '#99ff99', '#99ccff', '#cc99ff',
27
+ '#c00000', '#e36c09', '#5f497a', '#31859b', '#4f81bd', '#1f497d'
28
+ ];
29
+ const DEFAULT_TOOLBAR_CONFIG = {
30
+ history: true,
31
+ format: true,
32
+ fontSize: true,
33
+ fontName: true,
34
+ inline: {
35
+ bold: true,
36
+ italic: true,
37
+ underline: true,
38
+ strikeThrough: true,
39
+ superscript: true,
40
+ subscript: true,
41
+ code: true,
42
+ },
43
+ color: {
44
+ textColor: true,
45
+ backgroundColor: true,
46
+ },
47
+ alignment: {
48
+ left: true,
49
+ center: true,
50
+ right: true,
51
+ justify: true,
52
+ },
53
+ list: {
54
+ unordered: true,
55
+ ordered: true,
56
+ indent: true,
57
+ outdent: true,
58
+ },
59
+ insert: {
60
+ link: true,
61
+ image: true,
62
+ horizontalRule: true,
63
+ },
64
+ extra: {
65
+ clearFormat: true,
66
+ sourceView: true,
67
+ selectAll: true,
68
+ },
69
+ };
70
+ const DEFAULT_EDITOR_CONFIG = {
71
+ placeholder: 'Compose your content here...',
72
+ minHeight: '220px',
73
+ maxHeight: '600px',
74
+ height: 'auto',
75
+ fontFamily: '',
76
+ fontSize: '',
77
+ fontFamilies: DEFAULT_FONT_FAMILIES,
78
+ customClass: '',
79
+ editorStyle: '',
80
+ contentStyle: '',
81
+ spellcheck: true,
82
+ showToolbar: true,
83
+ showStatusBar: true,
84
+ showCharCount: true,
85
+ showWordCount: true,
86
+ colorPalette: DEFAULT_COLOR_PALETTE,
87
+ hashtagColor: '#0069FF',
88
+ highlightHashtags: true,
89
+ toolbar: DEFAULT_TOOLBAR_CONFIG,
90
+ };
91
+
92
+ const EDITOR_ICONS = {
93
+ undo: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7v6h6"></path><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"></path></svg>`,
94
+ redo: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M21 7v6h-6"></path><path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"></path></svg>`,
95
+ bold: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"></path><path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"></path></svg>`,
96
+ italic: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="4" x2="10" y2="4"></line><line x1="14" y1="20" x2="5" y2="20"></line><line x1="15" y1="4" x2="9" y2="20"></line></svg>`,
97
+ underline: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3"></path><line x1="4" y1="21" x2="20" y2="21"></line></svg>`,
98
+ strike: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M17.3 4.9A6 6 0 0 0 6 8c0 3 2 4 6 5s6 2 6 5a6 6 0 0 1-11.7 2"></path><line x1="2" y1="12" x2="22" y2="12"></line></svg>`,
99
+ subscript: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="m4 5 8 8"></path><path d="m12 5-8 8"></path><path d="M20 19h-4c0-1.5.44-2 1.5-2.5S20 15.33 20 14c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07"></path></svg>`,
100
+ superscript: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="m4 19 8-8"></path><path d="m12 19-8-8"></path><path d="M20 11h-4c0-1.5.44-2 1.5-2.5S20 7.33 20 6c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07"></path></svg>`,
101
+ code: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg>`,
102
+ textColor: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="m4 20 8-16 8 16"></path><line x1="6.8" y1="14.5" x2="17.2" y2="14.5"></line></svg>`,
103
+ bgColor: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="m19 11-8-8-8.6 8.6a2 2 0 0 0 0 2.8l5.2 5.2c.8.8 2 .8 2.8 0L19 11Z"></path><path d="m5 2 5 5"></path><path d="M2 22h20"></path></svg>`,
104
+ alignLeft: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="17" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="17" y1="18" x2="3" y2="18"></line></svg>`,
105
+ alignCenter: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="10" x2="6" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="18" y1="18" x2="6" y2="18"></line></svg>`,
106
+ alignRight: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="21" y1="10" x2="7" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="21" y1="18" x2="7" y2="18"></line></svg>`,
107
+ alignJustify: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="21" y1="10" x2="3" y2="10"></line><line x1="21" y1="6" x2="3" y2="6"></line><line x1="21" y1="14" x2="3" y2="14"></line><line x1="21" y1="18" x2="3" y2="18"></line></svg>`,
108
+ unorderedList: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="8" y1="6" x2="21" y2="6"></line><line x1="8" y1="12" x2="21" y2="12"></line><line x1="8" y1="18" x2="21" y2="18"></line><line x1="3" y1="6" x2="3.01" y2="6"></line><line x1="3" y1="12" x2="3.01" y2="12"></line><line x1="3" y1="18" x2="3.01" y2="18"></line></svg>`,
109
+ orderedList: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="10" y1="6" x2="21" y2="6"></line><line x1="10" y1="12" x2="21" y2="12"></line><line x1="10" y1="18" x2="21" y2="18"></line><path d="M4 6h1v4"></path><path d="M4 10h2"></path><path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"></path></svg>`,
110
+ indent: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 8 7 12 3 16"></polyline><line x1="21" y1="12" x2="11" y2="12"></line><line x1="21" y1="6" x2="11" y2="6"></line><line x1="21" y1="18" x2="11" y2="18"></line></svg>`,
111
+ outdent: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="7 8 3 12 7 16"></polyline><line x1="21" y1="12" x2="11" y2="12"></line><line x1="21" y1="6" x2="11" y2="6"></line><line x1="21" y1="18" x2="11" y2="18"></line></svg>`,
112
+ link: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>`,
113
+ unlink: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="m18.84 12.25 1.72-1.71a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="m5.16 11.75-1.72 1.71a5 5 0 0 0 7.07 7.07l1.72-1.71"></path><line x1="2" y1="2" x2="22" y2="22"></line></svg>`,
114
+ image: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg>`,
115
+ horizontalRule: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="7" y1="8" x2="17" y2="8" stroke-dasharray="2 2"></line><line x1="7" y1="16" x2="17" y2="16" stroke-dasharray="2 2"></line></svg>`,
116
+ clearFormat: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"></path><path d="M22 21H7"></path><path d="m5 11 9 9"></path></svg>`,
117
+ sourceView: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline><line x1="10" y1="20" x2="14" y2="4"></line></svg>`,
118
+ quote: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"></path><path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"></path></svg>`,
119
+ close: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`,
120
+ check: `<svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`,
121
+ chevronDown: `<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>`
122
+ };
123
+
124
+ class NgxEditorIconComponent {
125
+ name;
126
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NgxEditorIconComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
127
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NgxEditorIconComponent, isStandalone: true, selector: "ngx-editor-icon", inputs: { name: "name" }, ngImport: i0, template: `
128
+ @switch (name) {
129
+ @case ('bold') {
130
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
131
+ <path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"></path>
132
+ <path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"></path>
133
+ </svg>
134
+ }
135
+ @case ('italic') {
136
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
137
+ <line x1="19" y1="4" x2="10" y2="4"></line>
138
+ <line x1="14" y1="20" x2="5" y2="20"></line>
139
+ <line x1="15" y1="4" x2="9" y2="20"></line>
140
+ </svg>
141
+ }
142
+ @case ('underline') {
143
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
144
+ <path d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3"></path>
145
+ <line x1="4" y1="21" x2="20" y2="21"></line>
146
+ </svg>
147
+ }
148
+ @case ('strike') {
149
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
150
+ <path d="M17.3 4.9A6 6 0 0 0 6 8c0 3 2 4 6 5s6 2 6 5a6 6 0 0 1-11.7 2"></path>
151
+ <line x1="2" y1="12" x2="22" y2="12"></line>
152
+ </svg>
153
+ }
154
+ @case ('subscript') {
155
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
156
+ <path d="m4 5 8 8"></path>
157
+ <path d="m12 5-8 8"></path>
158
+ <path d="M20 19h-4c0-1.5.44-2 1.5-2.5S20 15.33 20 14c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07"></path>
159
+ </svg>
160
+ }
161
+ @case ('superscript') {
162
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
163
+ <path d="m4 19 8-8"></path>
164
+ <path d="m12 19-8-8"></path>
165
+ <path d="M20 11h-4c0-1.5.44-2 1.5-2.5S20 7.33 20 6c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07"></path>
166
+ </svg>
167
+ }
168
+ @case ('code') {
169
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
170
+ <polyline points="16 18 22 12 16 6"></polyline>
171
+ <polyline points="8 6 2 12 8 18"></polyline>
172
+ </svg>
173
+ }
174
+ @case ('unorderedList') {
175
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
176
+ <line x1="9" y1="6" x2="20" y2="6"></line>
177
+ <line x1="9" y1="12" x2="20" y2="12"></line>
178
+ <line x1="9" y1="18" x2="20" y2="18"></line>
179
+ <circle cx="4" cy="6" r="2" fill="currentColor"></circle>
180
+ <circle cx="4" cy="12" r="2" fill="currentColor"></circle>
181
+ <circle cx="4" cy="18" r="2" fill="currentColor"></circle>
182
+ </svg>
183
+ }
184
+ @case ('orderedList') {
185
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
186
+ <line x1="10" y1="6" x2="21" y2="6"></line>
187
+ <line x1="10" y1="12" x2="21" y2="12"></line>
188
+ <line x1="10" y1="18" x2="21" y2="18"></line>
189
+ <path d="M4 6h1v4"></path>
190
+ <path d="M4 10h2"></path>
191
+ <path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"></path>
192
+ </svg>
193
+ }
194
+ @case ('indent') {
195
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
196
+ <polyline points="3 8 7 12 3 16"></polyline>
197
+ <line x1="21" y1="12" x2="11" y2="12"></line>
198
+ <line x1="21" y1="6" x2="11" y2="6"></line>
199
+ <line x1="21" y1="18" x2="11" y2="18"></line>
200
+ </svg>
201
+ }
202
+ @case ('outdent') {
203
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
204
+ <polyline points="7 8 3 12 7 16"></polyline>
205
+ <line x1="21" y1="12" x2="11" y2="12"></line>
206
+ <line x1="21" y1="6" x2="11" y2="6"></line>
207
+ <line x1="21" y1="18" x2="11" y2="18"></line>
208
+ </svg>
209
+ }
210
+ @case ('alignLeft') {
211
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
212
+ <line x1="17" y1="10" x2="3" y2="10"></line>
213
+ <line x1="21" y1="6" x2="3" y2="6"></line>
214
+ <line x1="21" y1="14" x2="3" y2="14"></line>
215
+ <line x1="17" y1="18" x2="3" y2="18"></line>
216
+ </svg>
217
+ }
218
+ @case ('alignCenter') {
219
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
220
+ <line x1="18" y1="10" x2="6" y2="10"></line>
221
+ <line x1="21" y1="6" x2="3" y2="6"></line>
222
+ <line x1="21" y1="14" x2="3" y2="14"></line>
223
+ <line x1="18" y1="18" x2="6" y2="18"></line>
224
+ </svg>
225
+ }
226
+ @case ('alignRight') {
227
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
228
+ <line x1="21" y1="10" x2="7" y2="10"></line>
229
+ <line x1="21" y1="6" x2="3" y2="6"></line>
230
+ <line x1="21" y1="14" x2="3" y2="14"></line>
231
+ <line x1="21" y1="18" x2="7" y2="18"></line>
232
+ </svg>
233
+ }
234
+ @case ('alignJustify') {
235
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
236
+ <line x1="21" y1="10" x2="3" y2="10"></line>
237
+ <line x1="21" y1="6" x2="3" y2="6"></line>
238
+ <line x1="21" y1="14" x2="3" y2="14"></line>
239
+ <line x1="21" y1="18" x2="3" y2="18"></line>
240
+ </svg>
241
+ }
242
+ @case ('link') {
243
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
244
+ <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
245
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
246
+ </svg>
247
+ }
248
+ @case ('unlink') {
249
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
250
+ <path d="m18.84 12.25 1.72-1.71a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
251
+ <path d="m5.16 11.75-1.72 1.71a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
252
+ <line x1="2" y1="2" x2="22" y2="22"></line>
253
+ </svg>
254
+ }
255
+ @case ('image') {
256
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
257
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
258
+ <circle cx="8.5" cy="8.5" r="1.5"></circle>
259
+ <polyline points="21 15 16 10 5 21"></polyline>
260
+ </svg>
261
+ }
262
+ @case ('horizontalRule') {
263
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
264
+ <line x1="3" y1="12" x2="21" y2="12"></line>
265
+ <line x1="7" y1="8" x2="17" y2="8" stroke-dasharray="2 2"></line>
266
+ <line x1="7" y1="16" x2="17" y2="16" stroke-dasharray="2 2"></line>
267
+ </svg>
268
+ }
269
+ @case ('textColor') {
270
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
271
+ <path d="m4 20 8-16 8 16"></path>
272
+ <line x1="6.8" y1="14.5" x2="17.2" y2="14.5"></line>
273
+ </svg>
274
+ }
275
+ @case ('bgColor') {
276
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
277
+ <path d="m19 11-8-8-8.6 8.6a2 2 0 0 0 0 2.8l5.2 5.2c.8.8 2 .8 2.8 0L19 11Z"></path>
278
+ <path d="m5 2 5 5"></path>
279
+ <path d="M2 22h20"></path>
280
+ </svg>
281
+ }
282
+ @case ('undo') {
283
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
284
+ <path d="M3 7v6h6"></path>
285
+ <path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"></path>
286
+ </svg>
287
+ }
288
+ @case ('redo') {
289
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
290
+ <path d="M21 7v6h-6"></path>
291
+ <path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"></path>
292
+ </svg>
293
+ }
294
+ @case ('clearFormat') {
295
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
296
+ <path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"></path>
297
+ <path d="M22 21H7"></path>
298
+ <path d="m5 11 9 9"></path>
299
+ </svg>
300
+ }
301
+ @case ('sourceView') {
302
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
303
+ <polyline points="16 18 22 12 16 6"></polyline>
304
+ <polyline points="8 6 2 12 8 18"></polyline>
305
+ <line x1="10" y1="20" x2="14" y2="4"></line>
306
+ </svg>
307
+ }
308
+ @case ('emoji') {
309
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
310
+ <circle cx="12" cy="12" r="10"></circle>
311
+ <path d="M8 14s1.5 2 4 2 4-2 4-2"></path>
312
+ <line x1="9" y1="9" x2="9.01" y2="9"></line>
313
+ <line x1="15" y1="9" x2="15.01" y2="9"></line>
314
+ </svg>
315
+ }
316
+ @case ('close') {
317
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
318
+ <line x1="18" y1="6" x2="6" y2="18"></line>
319
+ <line x1="6" y1="6" x2="18" y2="18"></line>
320
+ </svg>
321
+ }
322
+ @default {
323
+ <span></span>
324
+ }
325
+ }
326
+ `, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;line-height:0;pointer-events:none}svg{display:block}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }] });
327
+ }
328
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NgxEditorIconComponent, decorators: [{
329
+ type: Component,
330
+ args: [{ selector: 'ngx-editor-icon', standalone: true, imports: [CommonModule], template: `
331
+ @switch (name) {
332
+ @case ('bold') {
333
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
334
+ <path d="M6 4h8a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"></path>
335
+ <path d="M6 12h9a4 4 0 0 1 4 4 4 4 0 0 1-4 4H6z"></path>
336
+ </svg>
337
+ }
338
+ @case ('italic') {
339
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
340
+ <line x1="19" y1="4" x2="10" y2="4"></line>
341
+ <line x1="14" y1="20" x2="5" y2="20"></line>
342
+ <line x1="15" y1="4" x2="9" y2="20"></line>
343
+ </svg>
344
+ }
345
+ @case ('underline') {
346
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
347
+ <path d="M6 3v7a6 6 0 0 0 6 6 6 6 0 0 0 6-6V3"></path>
348
+ <line x1="4" y1="21" x2="20" y2="21"></line>
349
+ </svg>
350
+ }
351
+ @case ('strike') {
352
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
353
+ <path d="M17.3 4.9A6 6 0 0 0 6 8c0 3 2 4 6 5s6 2 6 5a6 6 0 0 1-11.7 2"></path>
354
+ <line x1="2" y1="12" x2="22" y2="12"></line>
355
+ </svg>
356
+ }
357
+ @case ('subscript') {
358
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
359
+ <path d="m4 5 8 8"></path>
360
+ <path d="m12 5-8 8"></path>
361
+ <path d="M20 19h-4c0-1.5.44-2 1.5-2.5S20 15.33 20 14c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07"></path>
362
+ </svg>
363
+ }
364
+ @case ('superscript') {
365
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
366
+ <path d="m4 19 8-8"></path>
367
+ <path d="m12 19-8-8"></path>
368
+ <path d="M20 11h-4c0-1.5.44-2 1.5-2.5S20 7.33 20 6c0-.47-.17-.93-.48-1.29a2.11 2.11 0 0 0-2.62-.44c-.42.24-.74.62-.9 1.07"></path>
369
+ </svg>
370
+ }
371
+ @case ('code') {
372
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
373
+ <polyline points="16 18 22 12 16 6"></polyline>
374
+ <polyline points="8 6 2 12 8 18"></polyline>
375
+ </svg>
376
+ }
377
+ @case ('unorderedList') {
378
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
379
+ <line x1="9" y1="6" x2="20" y2="6"></line>
380
+ <line x1="9" y1="12" x2="20" y2="12"></line>
381
+ <line x1="9" y1="18" x2="20" y2="18"></line>
382
+ <circle cx="4" cy="6" r="2" fill="currentColor"></circle>
383
+ <circle cx="4" cy="12" r="2" fill="currentColor"></circle>
384
+ <circle cx="4" cy="18" r="2" fill="currentColor"></circle>
385
+ </svg>
386
+ }
387
+ @case ('orderedList') {
388
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
389
+ <line x1="10" y1="6" x2="21" y2="6"></line>
390
+ <line x1="10" y1="12" x2="21" y2="12"></line>
391
+ <line x1="10" y1="18" x2="21" y2="18"></line>
392
+ <path d="M4 6h1v4"></path>
393
+ <path d="M4 10h2"></path>
394
+ <path d="M6 18H4c0-1 2-2 2-3s-1-1.5-2-1"></path>
395
+ </svg>
396
+ }
397
+ @case ('indent') {
398
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
399
+ <polyline points="3 8 7 12 3 16"></polyline>
400
+ <line x1="21" y1="12" x2="11" y2="12"></line>
401
+ <line x1="21" y1="6" x2="11" y2="6"></line>
402
+ <line x1="21" y1="18" x2="11" y2="18"></line>
403
+ </svg>
404
+ }
405
+ @case ('outdent') {
406
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
407
+ <polyline points="7 8 3 12 7 16"></polyline>
408
+ <line x1="21" y1="12" x2="11" y2="12"></line>
409
+ <line x1="21" y1="6" x2="11" y2="6"></line>
410
+ <line x1="21" y1="18" x2="11" y2="18"></line>
411
+ </svg>
412
+ }
413
+ @case ('alignLeft') {
414
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
415
+ <line x1="17" y1="10" x2="3" y2="10"></line>
416
+ <line x1="21" y1="6" x2="3" y2="6"></line>
417
+ <line x1="21" y1="14" x2="3" y2="14"></line>
418
+ <line x1="17" y1="18" x2="3" y2="18"></line>
419
+ </svg>
420
+ }
421
+ @case ('alignCenter') {
422
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
423
+ <line x1="18" y1="10" x2="6" y2="10"></line>
424
+ <line x1="21" y1="6" x2="3" y2="6"></line>
425
+ <line x1="21" y1="14" x2="3" y2="14"></line>
426
+ <line x1="18" y1="18" x2="6" y2="18"></line>
427
+ </svg>
428
+ }
429
+ @case ('alignRight') {
430
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
431
+ <line x1="21" y1="10" x2="7" y2="10"></line>
432
+ <line x1="21" y1="6" x2="3" y2="6"></line>
433
+ <line x1="21" y1="14" x2="3" y2="14"></line>
434
+ <line x1="21" y1="18" x2="7" y2="18"></line>
435
+ </svg>
436
+ }
437
+ @case ('alignJustify') {
438
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
439
+ <line x1="21" y1="10" x2="3" y2="10"></line>
440
+ <line x1="21" y1="6" x2="3" y2="6"></line>
441
+ <line x1="21" y1="14" x2="3" y2="14"></line>
442
+ <line x1="21" y1="18" x2="3" y2="18"></line>
443
+ </svg>
444
+ }
445
+ @case ('link') {
446
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
447
+ <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
448
+ <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
449
+ </svg>
450
+ }
451
+ @case ('unlink') {
452
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
453
+ <path d="m18.84 12.25 1.72-1.71a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path>
454
+ <path d="m5.16 11.75-1.72 1.71a5 5 0 0 0 7.07 7.07l1.71-1.71"></path>
455
+ <line x1="2" y1="2" x2="22" y2="22"></line>
456
+ </svg>
457
+ }
458
+ @case ('image') {
459
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
460
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
461
+ <circle cx="8.5" cy="8.5" r="1.5"></circle>
462
+ <polyline points="21 15 16 10 5 21"></polyline>
463
+ </svg>
464
+ }
465
+ @case ('horizontalRule') {
466
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
467
+ <line x1="3" y1="12" x2="21" y2="12"></line>
468
+ <line x1="7" y1="8" x2="17" y2="8" stroke-dasharray="2 2"></line>
469
+ <line x1="7" y1="16" x2="17" y2="16" stroke-dasharray="2 2"></line>
470
+ </svg>
471
+ }
472
+ @case ('textColor') {
473
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
474
+ <path d="m4 20 8-16 8 16"></path>
475
+ <line x1="6.8" y1="14.5" x2="17.2" y2="14.5"></line>
476
+ </svg>
477
+ }
478
+ @case ('bgColor') {
479
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
480
+ <path d="m19 11-8-8-8.6 8.6a2 2 0 0 0 0 2.8l5.2 5.2c.8.8 2 .8 2.8 0L19 11Z"></path>
481
+ <path d="m5 2 5 5"></path>
482
+ <path d="M2 22h20"></path>
483
+ </svg>
484
+ }
485
+ @case ('undo') {
486
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
487
+ <path d="M3 7v6h6"></path>
488
+ <path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"></path>
489
+ </svg>
490
+ }
491
+ @case ('redo') {
492
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
493
+ <path d="M21 7v6h-6"></path>
494
+ <path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"></path>
495
+ </svg>
496
+ }
497
+ @case ('clearFormat') {
498
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
499
+ <path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"></path>
500
+ <path d="M22 21H7"></path>
501
+ <path d="m5 11 9 9"></path>
502
+ </svg>
503
+ }
504
+ @case ('sourceView') {
505
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
506
+ <polyline points="16 18 22 12 16 6"></polyline>
507
+ <polyline points="8 6 2 12 8 18"></polyline>
508
+ <line x1="10" y1="20" x2="14" y2="4"></line>
509
+ </svg>
510
+ }
511
+ @case ('emoji') {
512
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
513
+ <circle cx="12" cy="12" r="10"></circle>
514
+ <path d="M8 14s1.5 2 4 2 4-2 4-2"></path>
515
+ <line x1="9" y1="9" x2="9.01" y2="9"></line>
516
+ <line x1="15" y1="9" x2="15.01" y2="9"></line>
517
+ </svg>
518
+ }
519
+ @case ('close') {
520
+ <svg viewBox="0 0 24 24" width="16" height="16" stroke="currentColor" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round">
521
+ <line x1="18" y1="6" x2="6" y2="18"></line>
522
+ <line x1="6" y1="6" x2="18" y2="18"></line>
523
+ </svg>
524
+ }
525
+ @default {
526
+ <span></span>
527
+ }
528
+ }
529
+ `, styles: [":host{display:inline-flex;align-items:center;justify-content:center;line-height:0;pointer-events:none}svg{display:block}\n"] }]
530
+ }], propDecorators: { name: [{
531
+ type: Input,
532
+ args: [{ required: true }]
533
+ }] } });
534
+
535
+ class NgxEditorToolbarComponent {
536
+ hostRef;
537
+ editor;
538
+ tools = [
539
+ 'bold',
540
+ 'italic',
541
+ 'underline',
542
+ 'bulletList',
543
+ 'orderedList',
544
+ ];
545
+ fontFamilies;
546
+ actionTriggered = new EventEmitter();
547
+ showEmojiPicker = false;
548
+ defaultEmojis = [
549
+ '😊', '👍', '❤️', '🎉', '🚀', '✨', '🔥', '💡',
550
+ '👏', '🎯', '📝', '🌟', '💪', '🤝', '☕', '✅', '⚡', '💯'
551
+ ];
552
+ constructor(hostRef) {
553
+ this.hostRef = hostRef;
554
+ }
555
+ get resolvedFontFamilies() {
556
+ if (this.fontFamilies && this.fontFamilies.length > 0) {
557
+ return this.normalizeFonts(this.fontFamilies);
558
+ }
559
+ if (this.editor && this.editor.resolvedFontFamilies) {
560
+ return this.editor.resolvedFontFamilies;
561
+ }
562
+ return DEFAULT_FONT_FAMILIES;
563
+ }
564
+ normalizeFonts(list) {
565
+ return list.map((item) => {
566
+ if (typeof item === 'string') {
567
+ return { label: item, value: item.includes(',') ? item : `'${item}', sans-serif` };
568
+ }
569
+ return item;
570
+ });
571
+ }
572
+ isStringTool(tool) {
573
+ return typeof tool === 'string';
574
+ }
575
+ isFormatActive(format) {
576
+ if (this.editor && this.editor.isFormatActive) {
577
+ return this.editor.isFormatActive(format);
578
+ }
579
+ return false;
580
+ }
581
+ trigger(action) {
582
+ if (!this.editor) {
583
+ this.actionTriggered.emit(action);
584
+ return;
585
+ }
586
+ switch (action) {
587
+ case 'bold':
588
+ this.editor.bold();
589
+ break;
590
+ case 'italic':
591
+ this.editor.italic();
592
+ break;
593
+ case 'underline':
594
+ this.editor.underline();
595
+ break;
596
+ case 'strikeThrough':
597
+ this.editor.strike();
598
+ break;
599
+ case 'code':
600
+ this.editor.wrapSelectionWithCode();
601
+ break;
602
+ case 'bulletList':
603
+ this.editor.insertUnorderedList();
604
+ break;
605
+ case 'orderedList':
606
+ this.editor.insertOrderedList();
607
+ break;
608
+ case 'indent':
609
+ this.editor.indent();
610
+ break;
611
+ case 'outdent':
612
+ this.editor.outdent();
613
+ break;
614
+ case 'undo':
615
+ this.editor.undo();
616
+ break;
617
+ case 'redo':
618
+ this.editor.redo();
619
+ break;
620
+ case 'clearFormat':
621
+ this.editor.clearFormatting();
622
+ break;
623
+ case 'justifyLeft':
624
+ case 'alignLeft':
625
+ if (this.editor.alignLeft)
626
+ this.editor.alignLeft();
627
+ else
628
+ this.editor.exec('justifyLeft');
629
+ break;
630
+ case 'justifyCenter':
631
+ case 'alignCenter':
632
+ if (this.editor.alignCenter)
633
+ this.editor.alignCenter();
634
+ else
635
+ this.editor.exec('justifyCenter');
636
+ break;
637
+ case 'justifyRight':
638
+ case 'alignRight':
639
+ if (this.editor.alignRight)
640
+ this.editor.alignRight();
641
+ else
642
+ this.editor.exec('justifyRight');
643
+ break;
644
+ case 'justifyFull':
645
+ case 'alignJustify':
646
+ if (this.editor.alignJustify)
647
+ this.editor.alignJustify();
648
+ else
649
+ this.editor.exec('justifyFull');
650
+ break;
651
+ case 'subscript':
652
+ if (this.editor.subscript)
653
+ this.editor.subscript();
654
+ else
655
+ this.editor.exec('subscript');
656
+ break;
657
+ case 'superscript':
658
+ if (this.editor.superscript)
659
+ this.editor.superscript();
660
+ else
661
+ this.editor.exec('superscript');
662
+ break;
663
+ default:
664
+ this.editor.exec(action);
665
+ break;
666
+ }
667
+ this.actionTriggered.emit(action);
668
+ }
669
+ onFontFamilyChange(event) {
670
+ const select = event.target;
671
+ const font = select.value;
672
+ if (this.editor && this.editor.setFontFamily) {
673
+ this.editor.setFontFamily(font);
674
+ }
675
+ this.actionTriggered.emit(`fontFamily:${font}`);
676
+ }
677
+ onFontSizeChange(event) {
678
+ const select = event.target;
679
+ const size = select.value;
680
+ if (this.editor && this.editor.setFontSize) {
681
+ this.editor.setFontSize(size);
682
+ }
683
+ this.actionTriggered.emit(`fontSize:${size}`);
684
+ }
685
+ onCustomButtonClick(tool) {
686
+ if (tool.action) {
687
+ tool.action(this.editor);
688
+ }
689
+ }
690
+ toggleEmojiPicker() {
691
+ this.showEmojiPicker = !this.showEmojiPicker;
692
+ }
693
+ insertEmoji(emoji) {
694
+ if (this.editor) {
695
+ this.editor.insertEmoji(emoji);
696
+ }
697
+ this.showEmojiPicker = false;
698
+ }
699
+ onFormatBlockChange(event) {
700
+ if (this.editor && this.editor.onFormatBlockChange) {
701
+ this.editor.onFormatBlockChange(event);
702
+ }
703
+ }
704
+ onDocumentClick(event) {
705
+ if (this.showEmojiPicker && !this.hostRef.nativeElement.contains(event.target)) {
706
+ this.showEmojiPicker = false;
707
+ }
708
+ }
709
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NgxEditorToolbarComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
710
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NgxEditorToolbarComponent, isStandalone: true, selector: "ngx-editor-toolbar", inputs: { editor: "editor", tools: "tools", fontFamilies: "fontFamilies" }, outputs: { actionTriggered: "actionTriggered" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, ngImport: i0, template: "<div class=\"ngx-editor-toolbar\" role=\"toolbar\">\n @for (tool of tools; track $index) {\n @if (isStringTool(tool)) {\n @switch (tool) {\n @case ('|') {\n <div class=\"toolbar-divider\"></div>\n }\n\n @case ('undo') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Undo (Ctrl+Z)\"\n aria-label=\"Undo\"\n (mousedown)=\"$event.preventDefault(); trigger('undo')\"\n >\n <ngx-editor-icon name=\"undo\"></ngx-editor-icon>\n </button>\n }\n\n @case ('redo') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Redo (Ctrl+Y)\"\n aria-label=\"Redo\"\n (mousedown)=\"$event.preventDefault(); trigger('redo')\"\n >\n <ngx-editor-icon name=\"redo\"></ngx-editor-icon>\n </button>\n }\n\n @case ('bold') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('bold')\"\n title=\"Bold (Ctrl+B)\"\n aria-label=\"Bold\"\n (mousedown)=\"$event.preventDefault(); trigger('bold')\"\n >\n <ngx-editor-icon name=\"bold\"></ngx-editor-icon>\n </button>\n }\n\n @case ('italic') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('italic')\"\n title=\"Italic (Ctrl+I)\"\n aria-label=\"Italic\"\n (mousedown)=\"$event.preventDefault(); trigger('italic')\"\n >\n <ngx-editor-icon name=\"italic\"></ngx-editor-icon>\n </button>\n }\n\n @case ('underline') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('underline')\"\n title=\"Underline (Ctrl+U)\"\n aria-label=\"Underline\"\n (mousedown)=\"$event.preventDefault(); trigger('underline')\"\n >\n <ngx-editor-icon name=\"underline\"></ngx-editor-icon>\n </button>\n }\n\n @case ('strike') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('strikeThrough')\"\n title=\"Strikethrough\"\n aria-label=\"Strikethrough\"\n (mousedown)=\"$event.preventDefault(); trigger('strikeThrough')\"\n >\n <ngx-editor-icon name=\"strike\"></ngx-editor-icon>\n </button>\n }\n\n @case ('code') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Inline Code\"\n aria-label=\"Inline Code\"\n (mousedown)=\"$event.preventDefault(); trigger('code')\"\n >\n <ngx-editor-icon name=\"code\"></ngx-editor-icon>\n </button>\n }\n\n @case ('bulletList') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('insertUnorderedList')\"\n title=\"Bullet List\"\n aria-label=\"Bullet List\"\n (mousedown)=\"$event.preventDefault(); trigger('bulletList')\"\n >\n <ngx-editor-icon name=\"unorderedList\"></ngx-editor-icon>\n </button>\n }\n\n @case ('orderedList') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('insertOrderedList')\"\n title=\"Numbered List\"\n aria-label=\"Numbered List\"\n (mousedown)=\"$event.preventDefault(); trigger('orderedList')\"\n >\n <ngx-editor-icon name=\"orderedList\"></ngx-editor-icon>\n </button>\n }\n\n @case ('indent') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Increase Indent\"\n aria-label=\"Increase Indent\"\n (mousedown)=\"$event.preventDefault(); trigger('indent')\"\n >\n <ngx-editor-icon name=\"indent\"></ngx-editor-icon>\n </button>\n }\n\n @case ('outdent') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Decrease Indent\"\n aria-label=\"Decrease Indent\"\n (mousedown)=\"$event.preventDefault(); trigger('outdent')\"\n >\n <ngx-editor-icon name=\"outdent\"></ngx-editor-icon>\n </button>\n }\n\n @case ('emoji') {\n <div style=\"position: relative;\">\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Insert Emoji\"\n aria-label=\"Insert Emoji\"\n (click)=\"toggleEmojiPicker()\"\n >\n <ngx-editor-icon name=\"emoji\"></ngx-editor-icon>\n </button>\n @if (showEmojiPicker) {\n <div class=\"ngx-toolbar-popover emoji-popover\" (mousedown)=\"$event.stopPropagation()\">\n <div class=\"emoji-grid\">\n @for (emoji of defaultEmojis; track emoji) {\n <button\n type=\"button\"\n class=\"emoji-btn\"\n (click)=\"insertEmoji(emoji)\"\n >\n {{ emoji }}\n </button>\n }\n </div>\n </div>\n }\n </div>\n }\n\n @case ('formatBlock') {\n <select\n class=\"ngx-editor-select\"\n [value]=\"editor?.activeBlockTag || 'p'\"\n (change)=\"onFormatBlockChange($event)\"\n >\n <option value=\"p\">Normal text</option>\n <option value=\"h1\">Heading 1</option>\n <option value=\"h2\">Heading 2</option>\n <option value=\"h3\">Heading 3</option>\n <option value=\"h4\">Heading 4</option>\n <option value=\"blockquote\">Quote</option>\n <option value=\"pre\">Code Block</option>\n </select>\n }\n\n @case ('link') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Insert Link\"\n aria-label=\"Insert Link\"\n (click)=\"editor?.openLinkModal()\"\n >\n <ngx-editor-icon name=\"link\"></ngx-editor-icon>\n </button>\n }\n\n @case ('image') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Insert Image\"\n aria-label=\"Insert Image\"\n (click)=\"editor?.openImageModal()\"\n >\n <ngx-editor-icon name=\"image\"></ngx-editor-icon>\n </button>\n }\n\n @case ('clearFormat') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Clear Formatting\"\n aria-label=\"Clear Formatting\"\n (mousedown)=\"$event.preventDefault(); trigger('clearFormat')\"\n >\n <ngx-editor-icon name=\"clearFormat\"></ngx-editor-icon>\n </button>\n }\n\n @case ('fontFamily') {\n <select\n class=\"ngx-editor-select\"\n title=\"Font Family\"\n aria-label=\"Font Family\"\n (change)=\"onFontFamilyChange($event)\"\n >\n <option value=\"\">Font Family</option>\n @for (font of resolvedFontFamilies; track font.value) {\n <option [value]=\"font.value\">{{ font.label }}</option>\n }\n </select>\n }\n\n @case ('fontName') {\n <select\n class=\"ngx-editor-select\"\n title=\"Font Family\"\n aria-label=\"Font Family\"\n (change)=\"onFontFamilyChange($event)\"\n >\n <option value=\"\">Font Family</option>\n @for (font of resolvedFontFamilies; track font.value) {\n <option [value]=\"font.value\">{{ font.label }}</option>\n }\n </select>\n }\n\n @case ('fontSize') {\n <select\n class=\"ngx-editor-select\"\n title=\"Font Size\"\n aria-label=\"Font Size\"\n (change)=\"onFontSizeChange($event)\"\n >\n <option value=\"\">Size</option>\n <option value=\"1\">Small (10pt)</option>\n <option value=\"3\">Normal (12pt)</option>\n <option value=\"5\">Large (18pt)</option>\n <option value=\"7\">Huge (36pt)</option>\n </select>\n }\n\n @case ('alignLeft') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Align Left\"\n aria-label=\"Align Left\"\n (mousedown)=\"$event.preventDefault(); trigger('alignLeft')\"\n >\n <ngx-editor-icon name=\"alignLeft\"></ngx-editor-icon>\n </button>\n }\n\n @case ('alignCenter') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Align Center\"\n aria-label=\"Align Center\"\n (mousedown)=\"$event.preventDefault(); trigger('alignCenter')\"\n >\n <ngx-editor-icon name=\"alignCenter\"></ngx-editor-icon>\n </button>\n }\n\n @case ('alignRight') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Align Right\"\n aria-label=\"Align Right\"\n (mousedown)=\"$event.preventDefault(); trigger('alignRight')\"\n >\n <ngx-editor-icon name=\"alignRight\"></ngx-editor-icon>\n </button>\n }\n\n @case ('alignJustify') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Align Justify\"\n aria-label=\"Align Justify\"\n (mousedown)=\"$event.preventDefault(); trigger('alignJustify')\"\n >\n <ngx-editor-icon name=\"alignJustify\"></ngx-editor-icon>\n </button>\n }\n\n @case ('subscript') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Subscript\"\n aria-label=\"Subscript\"\n (mousedown)=\"$event.preventDefault(); trigger('subscript')\"\n >\n <ngx-editor-icon name=\"subscript\"></ngx-editor-icon>\n </button>\n }\n\n @case ('superscript') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Superscript\"\n aria-label=\"Superscript\"\n (mousedown)=\"$event.preventDefault(); trigger('superscript')\"\n >\n <ngx-editor-icon name=\"superscript\"></ngx-editor-icon>\n </button>\n }\n\n @case ('sourceView') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"editor?.isSourceView\"\n title=\"Toggle HTML Source\"\n aria-label=\"Toggle HTML Source\"\n (click)=\"editor?.toggleSourceView()\"\n >\n <ngx-editor-icon name=\"sourceView\"></ngx-editor-icon>\n </button>\n }\n }\n } @else {\n <!-- Custom Toolbar Button Object (e.g. \"\u2728 Write with AI\") -->\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [ngClass]=\"tool.customClass || ''\"\n [class.is-active]=\"tool.isActive ? tool.isActive(editor) : false\"\n [title]=\"tool.title || tool.label || ''\"\n (click)=\"onCustomButtonClick(tool)\"\n >\n @if (tool.icon) {\n <span class=\"btn-icon\">{{ tool.icon }}</span>\n }\n @if (tool.label) {\n <span class=\"btn-label\">{{ tool.label }}</span>\n }\n </button>\n }\n }\n</div>\n", styles: [":host{display:block;font-family:var(--ngx-editor-font-family, inherit)}.ngx-editor-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:8px 10px;font-family:var(--ngx-editor-font-family, inherit);background-color:var(--ngx-editor-toolbar-bg, #ffffff);border-bottom:1px solid var(--ngx-editor-border, #e2e8f0);-webkit-user-select:none;user-select:none;position:relative;z-index:10}.toolbar-divider{width:1px;height:20px;background-color:var(--ngx-editor-border, #e2e8f0);margin:0 4px}.ngx-editor-btn{display:inline-flex;align-items:center;justify-content:center;min-width:32px;height:32px;padding:0 6px;border:1px solid transparent;border-radius:6px;background:transparent;color:var(--ngx-editor-text, #334155);font-size:13.5px;cursor:pointer;transition:all .15s ease;outline:none;gap:6px}.ngx-editor-btn:hover:not(:disabled){background-color:var(--ngx-editor-toolbar-btn-hover, #f1f5f9);color:#0f172a}.ngx-editor-btn:active:not(:disabled),.ngx-editor-btn.is-active{background-color:var(--ngx-editor-toolbar-btn-active, #eff6ff);color:var(--ngx-editor-toolbar-btn-active-color, #2563eb);border-color:#bfdbfe}.ngx-editor-btn:disabled{opacity:.35;cursor:not-allowed}.btn-ai,.btn-custom-ai{background:#eff6ff;color:#2563eb;border:1px solid #bfdbfe;font-weight:500;padding:0 12px;border-radius:6px;font-size:13px}.btn-ai:hover,.btn-custom-ai:hover{background:#dbeafe;border-color:#93c5fd;color:#1d4ed8}.ngx-editor-select{height:32px;padding:0 8px;font-family:var(--ngx-editor-font-family, inherit);font-size:13px;color:var(--ngx-editor-text, #334155);background-color:#fff;border:1px solid var(--ngx-editor-border, #e2e8f0);border-radius:6px;outline:none;cursor:pointer}.ngx-editor-select:hover{border-color:#cbd5e1}.ngx-editor-select:focus{border-color:#3b82f6}.color-bar{position:absolute;bottom:4px;left:6px;right:6px;height:3px;border-radius:1px}.ngx-toolbar-popover{position:absolute;top:100%;margin-top:6px;background:#fff;border:1px solid var(--ngx-editor-border, #e2e8f0);border-radius:8px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;padding:10px;z-index:60}.ngx-toolbar-popover.emoji-popover{width:220px}.ngx-toolbar-popover.color-popover{width:176px}.color-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:4px;margin-bottom:8px}.color-swatch{width:22px;height:22px;border-radius:4px;border:1px solid rgba(0,0,0,.15);cursor:pointer;padding:0}.color-swatch:hover{transform:scale(1.15)}.custom-color-picker{display:flex;align-items:center;gap:8px;font-size:12px;color:#64748b}.custom-color-picker input[type=color]{width:28px;height:24px;border:1px solid #e2e8f0;border-radius:4px;padding:0;cursor:pointer;background:none}.emoji-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:6px}.emoji-btn{font-size:18px;background:none;border:none;padding:4px;border-radius:4px;cursor:pointer;transition:transform .1s ease,background-color .1s ease}.emoji-btn:hover{background-color:#f1f5f9;transform:scale(1.2)}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "component", type: NgxEditorIconComponent, selector: "ngx-editor-icon", inputs: ["name"] }] });
711
+ }
712
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NgxEditorToolbarComponent, decorators: [{
713
+ type: Component,
714
+ args: [{ selector: 'ngx-editor-toolbar', standalone: true, imports: [CommonModule, FormsModule, NgxEditorIconComponent], template: "<div class=\"ngx-editor-toolbar\" role=\"toolbar\">\n @for (tool of tools; track $index) {\n @if (isStringTool(tool)) {\n @switch (tool) {\n @case ('|') {\n <div class=\"toolbar-divider\"></div>\n }\n\n @case ('undo') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Undo (Ctrl+Z)\"\n aria-label=\"Undo\"\n (mousedown)=\"$event.preventDefault(); trigger('undo')\"\n >\n <ngx-editor-icon name=\"undo\"></ngx-editor-icon>\n </button>\n }\n\n @case ('redo') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Redo (Ctrl+Y)\"\n aria-label=\"Redo\"\n (mousedown)=\"$event.preventDefault(); trigger('redo')\"\n >\n <ngx-editor-icon name=\"redo\"></ngx-editor-icon>\n </button>\n }\n\n @case ('bold') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('bold')\"\n title=\"Bold (Ctrl+B)\"\n aria-label=\"Bold\"\n (mousedown)=\"$event.preventDefault(); trigger('bold')\"\n >\n <ngx-editor-icon name=\"bold\"></ngx-editor-icon>\n </button>\n }\n\n @case ('italic') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('italic')\"\n title=\"Italic (Ctrl+I)\"\n aria-label=\"Italic\"\n (mousedown)=\"$event.preventDefault(); trigger('italic')\"\n >\n <ngx-editor-icon name=\"italic\"></ngx-editor-icon>\n </button>\n }\n\n @case ('underline') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('underline')\"\n title=\"Underline (Ctrl+U)\"\n aria-label=\"Underline\"\n (mousedown)=\"$event.preventDefault(); trigger('underline')\"\n >\n <ngx-editor-icon name=\"underline\"></ngx-editor-icon>\n </button>\n }\n\n @case ('strike') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('strikeThrough')\"\n title=\"Strikethrough\"\n aria-label=\"Strikethrough\"\n (mousedown)=\"$event.preventDefault(); trigger('strikeThrough')\"\n >\n <ngx-editor-icon name=\"strike\"></ngx-editor-icon>\n </button>\n }\n\n @case ('code') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Inline Code\"\n aria-label=\"Inline Code\"\n (mousedown)=\"$event.preventDefault(); trigger('code')\"\n >\n <ngx-editor-icon name=\"code\"></ngx-editor-icon>\n </button>\n }\n\n @case ('bulletList') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('insertUnorderedList')\"\n title=\"Bullet List\"\n aria-label=\"Bullet List\"\n (mousedown)=\"$event.preventDefault(); trigger('bulletList')\"\n >\n <ngx-editor-icon name=\"unorderedList\"></ngx-editor-icon>\n </button>\n }\n\n @case ('orderedList') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isFormatActive('insertOrderedList')\"\n title=\"Numbered List\"\n aria-label=\"Numbered List\"\n (mousedown)=\"$event.preventDefault(); trigger('orderedList')\"\n >\n <ngx-editor-icon name=\"orderedList\"></ngx-editor-icon>\n </button>\n }\n\n @case ('indent') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Increase Indent\"\n aria-label=\"Increase Indent\"\n (mousedown)=\"$event.preventDefault(); trigger('indent')\"\n >\n <ngx-editor-icon name=\"indent\"></ngx-editor-icon>\n </button>\n }\n\n @case ('outdent') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Decrease Indent\"\n aria-label=\"Decrease Indent\"\n (mousedown)=\"$event.preventDefault(); trigger('outdent')\"\n >\n <ngx-editor-icon name=\"outdent\"></ngx-editor-icon>\n </button>\n }\n\n @case ('emoji') {\n <div style=\"position: relative;\">\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Insert Emoji\"\n aria-label=\"Insert Emoji\"\n (click)=\"toggleEmojiPicker()\"\n >\n <ngx-editor-icon name=\"emoji\"></ngx-editor-icon>\n </button>\n @if (showEmojiPicker) {\n <div class=\"ngx-toolbar-popover emoji-popover\" (mousedown)=\"$event.stopPropagation()\">\n <div class=\"emoji-grid\">\n @for (emoji of defaultEmojis; track emoji) {\n <button\n type=\"button\"\n class=\"emoji-btn\"\n (click)=\"insertEmoji(emoji)\"\n >\n {{ emoji }}\n </button>\n }\n </div>\n </div>\n }\n </div>\n }\n\n @case ('formatBlock') {\n <select\n class=\"ngx-editor-select\"\n [value]=\"editor?.activeBlockTag || 'p'\"\n (change)=\"onFormatBlockChange($event)\"\n >\n <option value=\"p\">Normal text</option>\n <option value=\"h1\">Heading 1</option>\n <option value=\"h2\">Heading 2</option>\n <option value=\"h3\">Heading 3</option>\n <option value=\"h4\">Heading 4</option>\n <option value=\"blockquote\">Quote</option>\n <option value=\"pre\">Code Block</option>\n </select>\n }\n\n @case ('link') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Insert Link\"\n aria-label=\"Insert Link\"\n (click)=\"editor?.openLinkModal()\"\n >\n <ngx-editor-icon name=\"link\"></ngx-editor-icon>\n </button>\n }\n\n @case ('image') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Insert Image\"\n aria-label=\"Insert Image\"\n (click)=\"editor?.openImageModal()\"\n >\n <ngx-editor-icon name=\"image\"></ngx-editor-icon>\n </button>\n }\n\n @case ('clearFormat') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Clear Formatting\"\n aria-label=\"Clear Formatting\"\n (mousedown)=\"$event.preventDefault(); trigger('clearFormat')\"\n >\n <ngx-editor-icon name=\"clearFormat\"></ngx-editor-icon>\n </button>\n }\n\n @case ('fontFamily') {\n <select\n class=\"ngx-editor-select\"\n title=\"Font Family\"\n aria-label=\"Font Family\"\n (change)=\"onFontFamilyChange($event)\"\n >\n <option value=\"\">Font Family</option>\n @for (font of resolvedFontFamilies; track font.value) {\n <option [value]=\"font.value\">{{ font.label }}</option>\n }\n </select>\n }\n\n @case ('fontName') {\n <select\n class=\"ngx-editor-select\"\n title=\"Font Family\"\n aria-label=\"Font Family\"\n (change)=\"onFontFamilyChange($event)\"\n >\n <option value=\"\">Font Family</option>\n @for (font of resolvedFontFamilies; track font.value) {\n <option [value]=\"font.value\">{{ font.label }}</option>\n }\n </select>\n }\n\n @case ('fontSize') {\n <select\n class=\"ngx-editor-select\"\n title=\"Font Size\"\n aria-label=\"Font Size\"\n (change)=\"onFontSizeChange($event)\"\n >\n <option value=\"\">Size</option>\n <option value=\"1\">Small (10pt)</option>\n <option value=\"3\">Normal (12pt)</option>\n <option value=\"5\">Large (18pt)</option>\n <option value=\"7\">Huge (36pt)</option>\n </select>\n }\n\n @case ('alignLeft') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Align Left\"\n aria-label=\"Align Left\"\n (mousedown)=\"$event.preventDefault(); trigger('alignLeft')\"\n >\n <ngx-editor-icon name=\"alignLeft\"></ngx-editor-icon>\n </button>\n }\n\n @case ('alignCenter') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Align Center\"\n aria-label=\"Align Center\"\n (mousedown)=\"$event.preventDefault(); trigger('alignCenter')\"\n >\n <ngx-editor-icon name=\"alignCenter\"></ngx-editor-icon>\n </button>\n }\n\n @case ('alignRight') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Align Right\"\n aria-label=\"Align Right\"\n (mousedown)=\"$event.preventDefault(); trigger('alignRight')\"\n >\n <ngx-editor-icon name=\"alignRight\"></ngx-editor-icon>\n </button>\n }\n\n @case ('alignJustify') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Align Justify\"\n aria-label=\"Align Justify\"\n (mousedown)=\"$event.preventDefault(); trigger('alignJustify')\"\n >\n <ngx-editor-icon name=\"alignJustify\"></ngx-editor-icon>\n </button>\n }\n\n @case ('subscript') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Subscript\"\n aria-label=\"Subscript\"\n (mousedown)=\"$event.preventDefault(); trigger('subscript')\"\n >\n <ngx-editor-icon name=\"subscript\"></ngx-editor-icon>\n </button>\n }\n\n @case ('superscript') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Superscript\"\n aria-label=\"Superscript\"\n (mousedown)=\"$event.preventDefault(); trigger('superscript')\"\n >\n <ngx-editor-icon name=\"superscript\"></ngx-editor-icon>\n </button>\n }\n\n @case ('sourceView') {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"editor?.isSourceView\"\n title=\"Toggle HTML Source\"\n aria-label=\"Toggle HTML Source\"\n (click)=\"editor?.toggleSourceView()\"\n >\n <ngx-editor-icon name=\"sourceView\"></ngx-editor-icon>\n </button>\n }\n }\n } @else {\n <!-- Custom Toolbar Button Object (e.g. \"\u2728 Write with AI\") -->\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [ngClass]=\"tool.customClass || ''\"\n [class.is-active]=\"tool.isActive ? tool.isActive(editor) : false\"\n [title]=\"tool.title || tool.label || ''\"\n (click)=\"onCustomButtonClick(tool)\"\n >\n @if (tool.icon) {\n <span class=\"btn-icon\">{{ tool.icon }}</span>\n }\n @if (tool.label) {\n <span class=\"btn-label\">{{ tool.label }}</span>\n }\n </button>\n }\n }\n</div>\n", styles: [":host{display:block;font-family:var(--ngx-editor-font-family, inherit)}.ngx-editor-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:8px 10px;font-family:var(--ngx-editor-font-family, inherit);background-color:var(--ngx-editor-toolbar-bg, #ffffff);border-bottom:1px solid var(--ngx-editor-border, #e2e8f0);-webkit-user-select:none;user-select:none;position:relative;z-index:10}.toolbar-divider{width:1px;height:20px;background-color:var(--ngx-editor-border, #e2e8f0);margin:0 4px}.ngx-editor-btn{display:inline-flex;align-items:center;justify-content:center;min-width:32px;height:32px;padding:0 6px;border:1px solid transparent;border-radius:6px;background:transparent;color:var(--ngx-editor-text, #334155);font-size:13.5px;cursor:pointer;transition:all .15s ease;outline:none;gap:6px}.ngx-editor-btn:hover:not(:disabled){background-color:var(--ngx-editor-toolbar-btn-hover, #f1f5f9);color:#0f172a}.ngx-editor-btn:active:not(:disabled),.ngx-editor-btn.is-active{background-color:var(--ngx-editor-toolbar-btn-active, #eff6ff);color:var(--ngx-editor-toolbar-btn-active-color, #2563eb);border-color:#bfdbfe}.ngx-editor-btn:disabled{opacity:.35;cursor:not-allowed}.btn-ai,.btn-custom-ai{background:#eff6ff;color:#2563eb;border:1px solid #bfdbfe;font-weight:500;padding:0 12px;border-radius:6px;font-size:13px}.btn-ai:hover,.btn-custom-ai:hover{background:#dbeafe;border-color:#93c5fd;color:#1d4ed8}.ngx-editor-select{height:32px;padding:0 8px;font-family:var(--ngx-editor-font-family, inherit);font-size:13px;color:var(--ngx-editor-text, #334155);background-color:#fff;border:1px solid var(--ngx-editor-border, #e2e8f0);border-radius:6px;outline:none;cursor:pointer}.ngx-editor-select:hover{border-color:#cbd5e1}.ngx-editor-select:focus{border-color:#3b82f6}.color-bar{position:absolute;bottom:4px;left:6px;right:6px;height:3px;border-radius:1px}.ngx-toolbar-popover{position:absolute;top:100%;margin-top:6px;background:#fff;border:1px solid var(--ngx-editor-border, #e2e8f0);border-radius:8px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;padding:10px;z-index:60}.ngx-toolbar-popover.emoji-popover{width:220px}.ngx-toolbar-popover.color-popover{width:176px}.color-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:4px;margin-bottom:8px}.color-swatch{width:22px;height:22px;border-radius:4px;border:1px solid rgba(0,0,0,.15);cursor:pointer;padding:0}.color-swatch:hover{transform:scale(1.15)}.custom-color-picker{display:flex;align-items:center;gap:8px;font-size:12px;color:#64748b}.custom-color-picker input[type=color]{width:28px;height:24px;border:1px solid #e2e8f0;border-radius:4px;padding:0;cursor:pointer;background:none}.emoji-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:6px}.emoji-btn{font-size:18px;background:none;border:none;padding:4px;border-radius:4px;cursor:pointer;transition:transform .1s ease,background-color .1s ease}.emoji-btn:hover{background-color:#f1f5f9;transform:scale(1.2)}\n"] }]
715
+ }], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { editor: [{
716
+ type: Input
717
+ }], tools: [{
718
+ type: Input
719
+ }], fontFamilies: [{
720
+ type: Input
721
+ }], actionTriggered: [{
722
+ type: Output
723
+ }], onDocumentClick: [{
724
+ type: HostListener,
725
+ args: ['document:click', ['$event']]
726
+ }] } });
727
+
728
+ class EditorCommandService {
729
+ savedRange = null;
730
+ saveSelection() {
731
+ const sel = window.getSelection();
732
+ if (sel && sel.rangeCount > 0) {
733
+ this.savedRange = sel.getRangeAt(0).cloneRange();
734
+ return this.savedRange;
735
+ }
736
+ return null;
737
+ }
738
+ restoreSelection(range) {
739
+ const rangeToRestore = range || this.savedRange;
740
+ if (!rangeToRestore)
741
+ return;
742
+ const sel = window.getSelection();
743
+ if (sel) {
744
+ sel.removeAllRanges();
745
+ sel.addRange(rangeToRestore);
746
+ }
747
+ }
748
+ execute(command, value = undefined) {
749
+ try {
750
+ return document.execCommand(command, false, value);
751
+ }
752
+ catch (e) {
753
+ console.warn(`[NgxRichTextEditor] execCommand "${command}" failed:`, e);
754
+ return false;
755
+ }
756
+ }
757
+ queryState(command) {
758
+ try {
759
+ return document.queryCommandState(command);
760
+ }
761
+ catch {
762
+ return false;
763
+ }
764
+ }
765
+ queryValue(command) {
766
+ try {
767
+ return document.queryCommandValue(command);
768
+ }
769
+ catch {
770
+ return '';
771
+ }
772
+ }
773
+ formatBlock(tag) {
774
+ this.execute('formatBlock', `<${tag}>`);
775
+ }
776
+ setFontSize(size) {
777
+ this.execute('fontSize', size);
778
+ }
779
+ setFontName(font) {
780
+ this.execute('fontName', font);
781
+ }
782
+ setTextColor(color) {
783
+ this.execute('foreColor', color);
784
+ }
785
+ setBackgroundColor(color) {
786
+ // Both hiliteColor and backColor are used across different browsers
787
+ if (!this.execute('hiliteColor', color)) {
788
+ this.execute('backColor', color);
789
+ }
790
+ }
791
+ insertHtml(html) {
792
+ if (document.queryCommandSupported && document.queryCommandSupported('insertHTML')) {
793
+ this.execute('insertHTML', html);
794
+ }
795
+ else {
796
+ const sel = window.getSelection();
797
+ if (sel && sel.rangeCount > 0) {
798
+ const range = sel.getRangeAt(0);
799
+ range.deleteContents();
800
+ const el = document.createElement('div');
801
+ el.innerHTML = html;
802
+ const frag = document.createDocumentFragment();
803
+ let node;
804
+ let lastNode = null;
805
+ while ((node = el.firstChild)) {
806
+ lastNode = frag.appendChild(node);
807
+ }
808
+ range.insertNode(frag);
809
+ if (lastNode) {
810
+ const newRange = range.cloneRange();
811
+ newRange.setStartAfter(lastNode);
812
+ newRange.collapse(true);
813
+ sel.removeAllRanges();
814
+ sel.addRange(newRange);
815
+ }
816
+ }
817
+ }
818
+ }
819
+ createLink(url, text, openInNewTab = true) {
820
+ const sel = window.getSelection();
821
+ if (!sel)
822
+ return;
823
+ if (text && (!sel.toString() || sel.isCollapsed)) {
824
+ const targetAttr = openInNewTab ? ' target="_blank" rel="noopener noreferrer"' : '';
825
+ const html = `<a href="${url}"${targetAttr}>${text}</a>`;
826
+ this.insertHtml(html);
827
+ }
828
+ else {
829
+ this.execute('createLink', url);
830
+ // If openInNewTab, find the newly created anchor tag and set target
831
+ if (openInNewTab && sel.anchorNode) {
832
+ let parent = sel.anchorNode.parentElement;
833
+ while (parent && parent.tagName !== 'A' && parent.contentEditable !== 'true') {
834
+ parent = parent.parentElement;
835
+ }
836
+ if (parent && parent.tagName === 'A') {
837
+ parent.setAttribute('target', '_blank');
838
+ parent.setAttribute('rel', 'noopener noreferrer');
839
+ }
840
+ }
841
+ }
842
+ }
843
+ removeLink() {
844
+ this.execute('unlink');
845
+ }
846
+ insertImage(src, alt = '') {
847
+ const html = `<img src="${src}" alt="${alt}" style="max-width: 100%; height: auto; border-radius: 4px; margin: 8px 0;" />`;
848
+ this.insertHtml(html);
849
+ }
850
+ insertHorizontalRule() {
851
+ this.execute('insertHorizontalRule');
852
+ }
853
+ clearFormatting() {
854
+ this.execute('removeFormat');
855
+ this.execute('formatBlock', '<p>');
856
+ }
857
+ countStats(content) {
858
+ if (!content)
859
+ return { words: 0, chars: 0 };
860
+ // Strip HTML tags for accurate counts
861
+ const tempDiv = document.createElement('div');
862
+ tempDiv.innerHTML = content;
863
+ const text = (tempDiv.textContent || tempDiv.innerText || '').trim();
864
+ const chars = text.length;
865
+ const words = text ? text.split(/\s+/).filter(w => w.length > 0).length : 0;
866
+ return { words, chars };
867
+ }
868
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: EditorCommandService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
869
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: EditorCommandService, providedIn: 'root' });
870
+ }
871
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: EditorCommandService, decorators: [{
872
+ type: Injectable,
873
+ args: [{
874
+ providedIn: 'root'
875
+ }]
876
+ }] });
877
+
878
+ class NgxRichTextEditorComponent {
879
+ commandService;
880
+ sanitizer;
881
+ hostRef;
882
+ cdr;
883
+ editorContentRef;
884
+ sourceTextareaRef;
885
+ value = '';
886
+ placeholder;
887
+ disabled = false;
888
+ readonly = false;
889
+ showToolbar = true;
890
+ showStatusBar = true;
891
+ height;
892
+ minHeight;
893
+ maxHeight;
894
+ tools;
895
+ maxCharacters;
896
+ fontFamily;
897
+ fontSize;
898
+ fontFamilies;
899
+ customClass = '';
900
+ editorStyle = '';
901
+ contentStyle = '';
902
+ hashtagColor = '#0069FF';
903
+ highlightHashtags = true;
904
+ config = {};
905
+ valueChange = new EventEmitter();
906
+ textChange = new EventEmitter();
907
+ editorFocus = new EventEmitter();
908
+ editorBlur = new EventEmitter();
909
+ computedConfig = { ...DEFAULT_EDITOR_CONFIG };
910
+ content = '';
911
+ isFocused = false;
912
+ isSourceView = false;
913
+ activeColorPicker = null;
914
+ selectedTextColor = '#000000';
915
+ selectedBgColor = 'transparent';
916
+ activeBlockTag = 'p';
917
+ showLinkModal = false;
918
+ linkModel = { text: '', url: '', newTab: true };
919
+ showImageModal = false;
920
+ imageModel = { url: '', alt: '' };
921
+ savedSelectionRange = null;
922
+ icons = EDITOR_ICONS;
923
+ activeStates = {
924
+ bold: false,
925
+ italic: false,
926
+ underline: false,
927
+ strikeThrough: false,
928
+ subscript: false,
929
+ superscript: false,
930
+ justifyLeft: false,
931
+ justifyCenter: false,
932
+ justifyRight: false,
933
+ justifyFull: false,
934
+ insertUnorderedList: false,
935
+ insertOrderedList: false,
936
+ link: false,
937
+ };
938
+ stats = { words: 0, chars: 0 };
939
+ onChange = () => { };
940
+ onTouched = () => { };
941
+ constructor(commandService, sanitizer, hostRef, cdr) {
942
+ this.commandService = commandService;
943
+ this.sanitizer = sanitizer;
944
+ this.hostRef = hostRef;
945
+ this.cdr = cdr;
946
+ }
947
+ ngOnInit() {
948
+ this.mergeConfig();
949
+ this.updateStats();
950
+ }
951
+ ngOnChanges(changes) {
952
+ if (changes['config'] ||
953
+ changes['tools'] ||
954
+ changes['maxCharacters'] ||
955
+ changes['fontFamily'] ||
956
+ changes['fontSize'] ||
957
+ changes['fontFamilies'] ||
958
+ changes['customClass'] ||
959
+ changes['editorStyle'] ||
960
+ changes['contentStyle'] ||
961
+ changes['hashtagColor'] ||
962
+ changes['highlightHashtags']) {
963
+ this.mergeConfig();
964
+ if (changes['hashtagColor'] && this.hashtagColor) {
965
+ this.setHashtagColor(this.hashtagColor);
966
+ }
967
+ }
968
+ if (changes['value'] && changes['value'].currentValue !== undefined) {
969
+ if (changes['value'].currentValue !== this.content) {
970
+ this.setContent(changes['value'].currentValue, false);
971
+ }
972
+ }
973
+ if (changes['placeholder'] && this.placeholder !== undefined) {
974
+ this.computedConfig.placeholder = this.placeholder;
975
+ }
976
+ }
977
+ ngOnDestroy() { }
978
+ get resolvedFontFamilies() {
979
+ const list = this.fontFamilies || this.computedConfig.fontFamilies || DEFAULT_FONT_FAMILIES;
980
+ return list.map((item) => {
981
+ if (typeof item === 'string') {
982
+ return { label: item, value: item.includes(',') ? item : `'${item}', sans-serif` };
983
+ }
984
+ return item;
985
+ });
986
+ }
987
+ get parsedEditorStyle() {
988
+ const base = typeof this.editorStyle === 'string'
989
+ ? this.parseStyleString(this.editorStyle)
990
+ : (this.editorStyle || {});
991
+ return base;
992
+ }
993
+ get parsedContentStyle() {
994
+ const base = typeof this.contentStyle === 'string'
995
+ ? this.parseStyleString(this.contentStyle)
996
+ : (this.contentStyle || {});
997
+ return base;
998
+ }
999
+ parseStyleString(styleStr) {
1000
+ if (!styleStr)
1001
+ return {};
1002
+ const result = {};
1003
+ styleStr.split(';').forEach((rule) => {
1004
+ const [key, val] = rule.split(':');
1005
+ if (key && val) {
1006
+ result[key.trim()] = val.trim();
1007
+ }
1008
+ });
1009
+ return result;
1010
+ }
1011
+ get isEmpty() {
1012
+ if (this.activeStates?.insertOrderedList || this.activeStates?.insertUnorderedList) {
1013
+ return false;
1014
+ }
1015
+ if (this.editorContentRef?.nativeElement) {
1016
+ const el = this.editorContentRef.nativeElement;
1017
+ if (el.querySelector('ol, ul, li, table, img, hr, blockquote, pre, iframe, video, audio')) {
1018
+ return false;
1019
+ }
1020
+ }
1021
+ if (!this.content)
1022
+ return true;
1023
+ if (/<(ol|ul|li|table|tr|td|th|img|hr|blockquote|pre|iframe|video|audio)[\s>/]/i.test(this.content)) {
1024
+ return false;
1025
+ }
1026
+ const stripped = this.content
1027
+ .replace(/&nbsp;/gi, ' ')
1028
+ .replace(/<[^>]*>/g, '')
1029
+ .trim();
1030
+ return stripped.length === 0;
1031
+ }
1032
+ // ControlValueAccessor methods
1033
+ writeValue(value) {
1034
+ this.setContent(value || '', false);
1035
+ }
1036
+ registerOnChange(fn) {
1037
+ this.onChange = fn;
1038
+ }
1039
+ registerOnTouched(fn) {
1040
+ this.onTouched = fn;
1041
+ }
1042
+ setDisabledState(isDisabled) {
1043
+ this.disabled = isDisabled;
1044
+ }
1045
+ mergeConfig() {
1046
+ const toolbar = {
1047
+ ...DEFAULT_TOOLBAR_CONFIG,
1048
+ ...(this.config.toolbar || {}),
1049
+ inline: {
1050
+ ...DEFAULT_TOOLBAR_CONFIG.inline,
1051
+ ...(this.config.toolbar?.inline || {}),
1052
+ },
1053
+ color: {
1054
+ ...DEFAULT_TOOLBAR_CONFIG.color,
1055
+ ...(this.config.toolbar?.color || {}),
1056
+ },
1057
+ alignment: {
1058
+ ...DEFAULT_TOOLBAR_CONFIG.alignment,
1059
+ ...(this.config.toolbar?.alignment || {}),
1060
+ },
1061
+ list: {
1062
+ ...DEFAULT_TOOLBAR_CONFIG.list,
1063
+ ...(this.config.toolbar?.list || {}),
1064
+ },
1065
+ insert: {
1066
+ ...DEFAULT_TOOLBAR_CONFIG.insert,
1067
+ ...(this.config.toolbar?.insert || {}),
1068
+ },
1069
+ extra: {
1070
+ ...DEFAULT_TOOLBAR_CONFIG.extra,
1071
+ ...(this.config.toolbar?.extra || {}),
1072
+ },
1073
+ };
1074
+ this.computedConfig = {
1075
+ ...DEFAULT_EDITOR_CONFIG,
1076
+ ...this.config,
1077
+ toolbar,
1078
+ };
1079
+ if (this.placeholder) {
1080
+ this.computedConfig.placeholder = this.placeholder;
1081
+ }
1082
+ if (this.showToolbar !== undefined) {
1083
+ this.computedConfig.showToolbar = this.showToolbar;
1084
+ }
1085
+ if (this.showStatusBar !== undefined) {
1086
+ this.computedConfig.showStatusBar = this.showStatusBar;
1087
+ }
1088
+ if (this.height !== undefined) {
1089
+ this.computedConfig.height = this.height;
1090
+ }
1091
+ if (this.minHeight !== undefined) {
1092
+ this.computedConfig.minHeight = this.minHeight;
1093
+ }
1094
+ if (this.maxHeight !== undefined) {
1095
+ this.computedConfig.maxHeight = this.maxHeight;
1096
+ }
1097
+ if (this.tools) {
1098
+ this.computedConfig.tools = this.tools;
1099
+ }
1100
+ if (this.maxCharacters !== undefined) {
1101
+ this.computedConfig.maxCharacters = this.maxCharacters;
1102
+ }
1103
+ if (this.fontFamily !== undefined) {
1104
+ this.computedConfig.fontFamily = this.fontFamily;
1105
+ }
1106
+ if (this.fontSize !== undefined) {
1107
+ this.computedConfig.fontSize = this.fontSize;
1108
+ }
1109
+ if (this.fontFamilies !== undefined) {
1110
+ this.computedConfig.fontFamilies = this.fontFamilies;
1111
+ }
1112
+ if (this.customClass) {
1113
+ this.computedConfig.customClass = this.customClass;
1114
+ }
1115
+ if (this.editorStyle) {
1116
+ this.computedConfig.editorStyle = this.editorStyle;
1117
+ }
1118
+ if (this.contentStyle) {
1119
+ this.computedConfig.contentStyle = this.contentStyle;
1120
+ }
1121
+ if (this.hashtagColor !== undefined) {
1122
+ this.computedConfig.hashtagColor = this.hashtagColor;
1123
+ }
1124
+ if (this.highlightHashtags !== undefined) {
1125
+ this.computedConfig.highlightHashtags = this.highlightHashtags;
1126
+ }
1127
+ }
1128
+ setContent(newContent, emit = true) {
1129
+ this.content = newContent;
1130
+ if (this.editorContentRef && this.editorContentRef.nativeElement) {
1131
+ if (this.editorContentRef.nativeElement.innerHTML !== newContent) {
1132
+ this.editorContentRef.nativeElement.innerHTML = newContent;
1133
+ if (this.highlightHashtags) {
1134
+ this.highlightHashtagsInElement();
1135
+ this.content = this.editorContentRef.nativeElement.innerHTML;
1136
+ }
1137
+ }
1138
+ }
1139
+ this.updateStats();
1140
+ this.cdr.markForCheck();
1141
+ if (emit) {
1142
+ this.onChange(this.content);
1143
+ this.valueChange.emit(this.content);
1144
+ const text = this.editorContentRef?.nativeElement?.innerText || '';
1145
+ this.textChange.emit({ html: this.content, text });
1146
+ }
1147
+ }
1148
+ onEditorInput() {
1149
+ if (this.editorContentRef && this.editorContentRef.nativeElement) {
1150
+ const root = this.editorContentRef.nativeElement;
1151
+ if (this.highlightHashtags) {
1152
+ const spans = root.querySelectorAll('.ngx-editor-hashtag, .post-content-hashtag');
1153
+ let needsCleanup = false;
1154
+ for (let i = 0; i < spans.length; i++) {
1155
+ const text = spans[i].textContent || '';
1156
+ if (!text.match(/^#([a-zA-Z0-9_\u0080-\uFFFF]+)$/)) {
1157
+ needsCleanup = true;
1158
+ break;
1159
+ }
1160
+ }
1161
+ if (needsCleanup) {
1162
+ const offset = this.getCaretCharacterOffsetWithin(root);
1163
+ this.sanitizeHashtags(root);
1164
+ this.setCaretCharacterOffsetWithin(root, offset);
1165
+ }
1166
+ }
1167
+ const html = root.innerHTML;
1168
+ this.setContent(html, true);
1169
+ }
1170
+ }
1171
+ onSourceInput(event) {
1172
+ const textarea = event.target;
1173
+ this.setContent(textarea.value, true);
1174
+ }
1175
+ onEditorFocus(event) {
1176
+ this.isFocused = true;
1177
+ this.editorFocus.emit(event);
1178
+ }
1179
+ onEditorBlur(event) {
1180
+ this.isFocused = false;
1181
+ this.onTouched();
1182
+ if (this.highlightHashtags) {
1183
+ this.highlightHashtagsInElement();
1184
+ this.content = this.editorContentRef?.nativeElement?.innerHTML || '';
1185
+ }
1186
+ this.editorBlur.emit(event);
1187
+ }
1188
+ onEditorPaste() {
1189
+ if (this.highlightHashtags) {
1190
+ setTimeout(() => {
1191
+ this.highlightHashtagsInElement();
1192
+ this.onEditorInput();
1193
+ });
1194
+ }
1195
+ }
1196
+ onEditorKeydown(event) {
1197
+ const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
1198
+ const modifier = isMac ? event.metaKey : event.ctrlKey;
1199
+ if (!modifier && (event.key === ' ' || event.key === 'Enter')) {
1200
+ this.handleHashtagOnKey(event);
1201
+ }
1202
+ if (modifier) {
1203
+ switch (event.key.toLowerCase()) {
1204
+ case 'b':
1205
+ event.preventDefault();
1206
+ this.bold();
1207
+ break;
1208
+ case 'i':
1209
+ event.preventDefault();
1210
+ this.italic();
1211
+ break;
1212
+ case 'u':
1213
+ event.preventDefault();
1214
+ this.underline();
1215
+ break;
1216
+ case 'z':
1217
+ event.preventDefault();
1218
+ if (event.shiftKey) {
1219
+ this.redo();
1220
+ }
1221
+ else {
1222
+ this.undo();
1223
+ }
1224
+ break;
1225
+ case 'y':
1226
+ event.preventDefault();
1227
+ this.redo();
1228
+ break;
1229
+ case 'k':
1230
+ event.preventDefault();
1231
+ this.openLinkModal();
1232
+ break;
1233
+ }
1234
+ }
1235
+ else if (event.key === 'Tab') {
1236
+ event.preventDefault();
1237
+ if (event.shiftKey) {
1238
+ this.outdent();
1239
+ }
1240
+ else {
1241
+ this.indent();
1242
+ }
1243
+ }
1244
+ }
1245
+ /* =========================================================
1246
+ * PUBLIC CALLABLE API METHODS
1247
+ * Allows external HTML buttons, custom toolbars, and users
1248
+ * to invoke formatting directly: editor.bold(), editor.italic(), etc.
1249
+ * ========================================================= */
1250
+ exec(command, value = undefined) {
1251
+ if (this.disabled || this.readonly || this.isSourceView)
1252
+ return;
1253
+ this.focusEditor();
1254
+ this.commandService.execute(command, value);
1255
+ this.onEditorInput();
1256
+ this.updateEditorState();
1257
+ }
1258
+ bold() {
1259
+ this.exec('bold');
1260
+ }
1261
+ italic() {
1262
+ this.exec('italic');
1263
+ }
1264
+ underline() {
1265
+ this.exec('underline');
1266
+ }
1267
+ strike() {
1268
+ this.exec('strikeThrough');
1269
+ }
1270
+ subscript() {
1271
+ this.exec('subscript');
1272
+ }
1273
+ superscript() {
1274
+ this.exec('superscript');
1275
+ }
1276
+ setParagraph() {
1277
+ this.formatBlock('p');
1278
+ }
1279
+ setHeading(level) {
1280
+ this.formatBlock(`h${level}`);
1281
+ }
1282
+ setBlockquote() {
1283
+ this.formatBlock('blockquote');
1284
+ }
1285
+ setCodeBlock() {
1286
+ this.formatBlock('pre');
1287
+ }
1288
+ formatBlock(tag) {
1289
+ if (this.disabled || this.readonly || this.isSourceView)
1290
+ return;
1291
+ this.focusEditor();
1292
+ this.commandService.formatBlock(tag);
1293
+ this.onEditorInput();
1294
+ this.updateEditorState();
1295
+ }
1296
+ insertUnorderedList() {
1297
+ this.exec('insertUnorderedList');
1298
+ }
1299
+ insertOrderedList() {
1300
+ this.exec('insertOrderedList');
1301
+ }
1302
+ indent() {
1303
+ this.exec('indent');
1304
+ }
1305
+ outdent() {
1306
+ this.exec('outdent');
1307
+ }
1308
+ alignLeft() {
1309
+ this.exec('justifyLeft');
1310
+ }
1311
+ alignCenter() {
1312
+ this.exec('justifyCenter');
1313
+ }
1314
+ alignRight() {
1315
+ this.exec('justifyRight');
1316
+ }
1317
+ alignJustify() {
1318
+ this.exec('justifyFull');
1319
+ }
1320
+ undo() {
1321
+ this.exec('undo');
1322
+ }
1323
+ redo() {
1324
+ this.exec('redo');
1325
+ }
1326
+ clearFormatting() {
1327
+ if (this.disabled || this.readonly || this.isSourceView)
1328
+ return;
1329
+ this.focusEditor();
1330
+ this.commandService.clearFormatting();
1331
+ this.onEditorInput();
1332
+ this.updateEditorState();
1333
+ }
1334
+ insertHtml(html) {
1335
+ if (this.disabled || this.readonly || this.isSourceView)
1336
+ return;
1337
+ this.focusEditor();
1338
+ this.commandService.insertHtml(html);
1339
+ this.onEditorInput();
1340
+ this.updateEditorState();
1341
+ }
1342
+ insertText(text) {
1343
+ this.insertHtml(text);
1344
+ }
1345
+ insertEmoji(emoji) {
1346
+ this.insertHtml(emoji);
1347
+ }
1348
+ insertLink(url, text, openInNewTab = true) {
1349
+ if (this.disabled || this.readonly || this.isSourceView)
1350
+ return;
1351
+ this.focusEditor();
1352
+ this.commandService.createLink(url, text, openInNewTab);
1353
+ this.onEditorInput();
1354
+ this.updateEditorState();
1355
+ }
1356
+ unlink() {
1357
+ this.exec('unlink');
1358
+ }
1359
+ insertImage(src, alt = '') {
1360
+ if (this.disabled || this.readonly || this.isSourceView)
1361
+ return;
1362
+ this.focusEditor();
1363
+ this.commandService.insertImage(src, alt);
1364
+ this.onEditorInput();
1365
+ this.updateEditorState();
1366
+ }
1367
+ insertHorizontalRule() {
1368
+ this.exec('insertHorizontalRule');
1369
+ }
1370
+ setFontFamily(font) {
1371
+ if (this.disabled || this.readonly || this.isSourceView || !font)
1372
+ return;
1373
+ this.focusEditor();
1374
+ this.commandService.setFontName(font);
1375
+ this.onEditorInput();
1376
+ this.updateEditorState();
1377
+ }
1378
+ setFontName(font) {
1379
+ this.setFontFamily(font);
1380
+ }
1381
+ setFontSize(size) {
1382
+ if (this.disabled || this.readonly || this.isSourceView || !size)
1383
+ return;
1384
+ this.focusEditor();
1385
+ this.commandService.setFontSize(String(size));
1386
+ this.onEditorInput();
1387
+ this.updateEditorState();
1388
+ }
1389
+ setTextColor(color) {
1390
+ if (this.disabled || this.readonly || this.isSourceView || !color)
1391
+ return;
1392
+ this.focusEditor();
1393
+ this.commandService.setTextColor(color);
1394
+ this.onEditorInput();
1395
+ this.updateEditorState();
1396
+ }
1397
+ setBackgroundColor(color) {
1398
+ if (this.disabled || this.readonly || this.isSourceView || !color)
1399
+ return;
1400
+ const normalizedColor = color.toLowerCase() === '#transparent' ? 'transparent' : color;
1401
+ this.focusEditor();
1402
+ this.commandService.setBackgroundColor(normalizedColor);
1403
+ this.onEditorInput();
1404
+ this.updateEditorState();
1405
+ }
1406
+ setHashtagColor(color) {
1407
+ if (!color)
1408
+ return;
1409
+ this.hashtagColor = color;
1410
+ this.computedConfig.hashtagColor = color;
1411
+ if (this.editorContentRef && this.editorContentRef.nativeElement) {
1412
+ this.editorContentRef.nativeElement.style.setProperty('--ngx-editor-hashtag-color', color);
1413
+ }
1414
+ }
1415
+ getCaretCharacterOffsetWithin(element) {
1416
+ let caretOffset = 0;
1417
+ const sel = window.getSelection();
1418
+ if (sel && sel.rangeCount > 0) {
1419
+ const range = sel.getRangeAt(0);
1420
+ const preCaretRange = range.cloneRange();
1421
+ preCaretRange.selectNodeContents(element);
1422
+ try {
1423
+ preCaretRange.setEnd(range.endContainer, range.endOffset);
1424
+ caretOffset = preCaretRange.toString().length;
1425
+ }
1426
+ catch {
1427
+ caretOffset = 0;
1428
+ }
1429
+ }
1430
+ return caretOffset;
1431
+ }
1432
+ setCaretCharacterOffsetWithin(element, offset) {
1433
+ const sel = window.getSelection();
1434
+ if (!sel)
1435
+ return;
1436
+ let currentOffset = 0;
1437
+ const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null);
1438
+ let node;
1439
+ while ((node = walker.nextNode())) {
1440
+ const textLength = node.textContent?.length || 0;
1441
+ if (currentOffset + textLength >= offset) {
1442
+ const range = document.createRange();
1443
+ range.setStart(node, Math.max(0, Math.min(offset - currentOffset, textLength)));
1444
+ range.collapse(true);
1445
+ sel.removeAllRanges();
1446
+ sel.addRange(range);
1447
+ return;
1448
+ }
1449
+ currentOffset += textLength;
1450
+ }
1451
+ if (element.childNodes.length > 0) {
1452
+ const range = document.createRange();
1453
+ range.selectNodeContents(element);
1454
+ range.collapse(false);
1455
+ sel.removeAllRanges();
1456
+ sel.addRange(range);
1457
+ }
1458
+ }
1459
+ sanitizeHashtags(root) {
1460
+ if (!this.highlightHashtags)
1461
+ return false;
1462
+ let modified = false;
1463
+ // 1. Unwrap any hashtag spans that do NOT match a valid hashtag (#word)
1464
+ const existingSpans = Array.from(root.querySelectorAll('.ngx-editor-hashtag, .post-content-hashtag'));
1465
+ for (const span of existingSpans) {
1466
+ if (span.hasAttribute('contenteditable')) {
1467
+ span.removeAttribute('contenteditable');
1468
+ modified = true;
1469
+ }
1470
+ const text = span.textContent || '';
1471
+ const exactMatch = text.match(/^#([a-zA-Z0-9_\u0080-\uFFFF]+)$/);
1472
+ if (exactMatch) {
1473
+ if (span.style.color) {
1474
+ span.style.removeProperty('color');
1475
+ modified = true;
1476
+ }
1477
+ continue;
1478
+ }
1479
+ modified = true;
1480
+ // If user typed text after the hashtag inside the span (e.g. "#hello how are you")
1481
+ const partialMatch = text.match(/^(#([a-zA-Z0-9_\u0080-\uFFFF]+))(.*)$/s);
1482
+ if (partialMatch && partialMatch[3].length > 0) {
1483
+ span.textContent = partialMatch[1];
1484
+ if (span.style.color) {
1485
+ span.style.removeProperty('color');
1486
+ }
1487
+ const remainingNode = document.createTextNode(partialMatch[3]);
1488
+ span.parentNode?.insertBefore(remainingNode, span.nextSibling);
1489
+ }
1490
+ else {
1491
+ // Not a hashtag at all (e.g. "sff" from the screenshot)! Unwrap completely!
1492
+ const parent = span.parentNode;
1493
+ if (parent) {
1494
+ while (span.firstChild) {
1495
+ parent.insertBefore(span.firstChild, span);
1496
+ }
1497
+ parent.removeChild(span);
1498
+ }
1499
+ }
1500
+ }
1501
+ // 2. Format plain text nodes that contain hashtags
1502
+ const hashtagRegex = /(?:^|[\s\u00A0])(#([a-zA-Z0-9_\u0080-\uFFFF]+))/g;
1503
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
1504
+ acceptNode(node) {
1505
+ const parent = node.parentElement;
1506
+ if (!parent)
1507
+ return NodeFilter.FILTER_REJECT;
1508
+ const tag = parent.tagName.toLowerCase();
1509
+ if (tag === 'a' || tag === 'script' || tag === 'style') {
1510
+ return NodeFilter.FILTER_REJECT;
1511
+ }
1512
+ if (parent.classList.contains('ngx-editor-hashtag') || parent.classList.contains('post-content-hashtag')) {
1513
+ return NodeFilter.FILTER_REJECT;
1514
+ }
1515
+ return NodeFilter.FILTER_ACCEPT;
1516
+ },
1517
+ });
1518
+ const nodesToReplace = [];
1519
+ let currentNode;
1520
+ while ((currentNode = walker.nextNode())) {
1521
+ if (hashtagRegex.test(currentNode.textContent || '')) {
1522
+ nodesToReplace.push(currentNode);
1523
+ }
1524
+ hashtagRegex.lastIndex = 0;
1525
+ }
1526
+ for (const textNode of nodesToReplace) {
1527
+ const text = textNode.textContent || '';
1528
+ const fragment = document.createDocumentFragment();
1529
+ let lastIndex = 0;
1530
+ let match;
1531
+ while ((match = hashtagRegex.exec(text)) !== null) {
1532
+ const matchIndex = match.index;
1533
+ const fullMatch = match[0];
1534
+ const hasLeadingSpace = !fullMatch.startsWith('#');
1535
+ const tagPos = matchIndex + (hasLeadingSpace ? 1 : 0);
1536
+ const fullHashtag = match[1].trim();
1537
+ if (tagPos > lastIndex) {
1538
+ fragment.appendChild(document.createTextNode(text.substring(lastIndex, tagPos)));
1539
+ }
1540
+ const span = document.createElement('span');
1541
+ span.className = 'ngx-editor-hashtag post-content-hashtag';
1542
+ span.textContent = fullHashtag;
1543
+ fragment.appendChild(span);
1544
+ lastIndex = tagPos + fullHashtag.length;
1545
+ }
1546
+ if (lastIndex < text.length) {
1547
+ fragment.appendChild(document.createTextNode(text.substring(lastIndex)));
1548
+ }
1549
+ textNode.parentNode?.replaceChild(fragment, textNode);
1550
+ modified = true;
1551
+ }
1552
+ return modified;
1553
+ }
1554
+ highlightHashtagsInElement() {
1555
+ if (this.editorContentRef && this.editorContentRef.nativeElement) {
1556
+ this.sanitizeHashtags(this.editorContentRef.nativeElement);
1557
+ }
1558
+ }
1559
+ handleHashtagOnKey(event) {
1560
+ if (!this.highlightHashtags)
1561
+ return;
1562
+ const sel = window.getSelection();
1563
+ if (!sel || !sel.rangeCount)
1564
+ return;
1565
+ const range = sel.getRangeAt(0);
1566
+ // Case 1: Enter key inside or right after a hashtag span
1567
+ if (event.key === 'Enter') {
1568
+ let node = range.startContainer;
1569
+ let hashtagSpan = null;
1570
+ while (node && node !== this.editorContentRef?.nativeElement) {
1571
+ if (node instanceof HTMLElement &&
1572
+ (node.classList.contains('ngx-editor-hashtag') || node.classList.contains('post-content-hashtag'))) {
1573
+ hashtagSpan = node;
1574
+ break;
1575
+ }
1576
+ node = node.parentElement;
1577
+ }
1578
+ if (hashtagSpan && hashtagSpan.parentNode) {
1579
+ event.preventDefault();
1580
+ const br = document.createElement('br');
1581
+ hashtagSpan.parentNode.insertBefore(br, hashtagSpan.nextSibling);
1582
+ const newRange = document.createRange();
1583
+ newRange.setStartAfter(br);
1584
+ newRange.collapse(true);
1585
+ sel.removeAllRanges();
1586
+ sel.addRange(newRange);
1587
+ this.onEditorInput();
1588
+ return;
1589
+ }
1590
+ if (node && node.nodeType === Node.TEXT_NODE) {
1591
+ const offset = range.startOffset;
1592
+ const textBefore = (node.textContent || '').substring(0, offset);
1593
+ const match = textBefore.match(/(?:^|[\s\u00A0])(#([a-zA-Z0-9_\u0080-\uFFFF]+))$/);
1594
+ if (match) {
1595
+ event.preventDefault();
1596
+ const fullTag = match[1];
1597
+ const tagStartIndex = offset - fullTag.length;
1598
+ const tagRange = document.createRange();
1599
+ tagRange.setStart(node, tagStartIndex);
1600
+ tagRange.setEnd(node, offset);
1601
+ tagRange.deleteContents();
1602
+ const span = document.createElement('span');
1603
+ span.className = 'ngx-editor-hashtag post-content-hashtag';
1604
+ span.textContent = fullTag;
1605
+ tagRange.insertNode(span);
1606
+ const br = document.createElement('br');
1607
+ span.parentNode?.insertBefore(br, span.nextSibling);
1608
+ const newRange = document.createRange();
1609
+ newRange.setStartAfter(br);
1610
+ newRange.collapse(true);
1611
+ sel.removeAllRanges();
1612
+ sel.addRange(newRange);
1613
+ this.onEditorInput();
1614
+ return;
1615
+ }
1616
+ }
1617
+ return;
1618
+ }
1619
+ // Case 2: Space key right after typing a hashtag or inside a hashtag span
1620
+ if (event.key === ' ') {
1621
+ const node = range.startContainer;
1622
+ const offset = range.startOffset;
1623
+ if (node.nodeType !== Node.TEXT_NODE)
1624
+ return;
1625
+ const parent = node.parentElement;
1626
+ // Sub-case A: Caret is inside an existing hashtag span
1627
+ if (parent?.classList.contains('ngx-editor-hashtag') || parent?.classList.contains('post-content-hashtag')) {
1628
+ event.preventDefault();
1629
+ const text = node.textContent || '';
1630
+ const beforeCaret = text.substring(0, offset);
1631
+ const afterCaret = text.substring(offset);
1632
+ parent.textContent = beforeCaret;
1633
+ const afterNode = document.createTextNode('\u00A0' + afterCaret);
1634
+ parent.parentNode?.insertBefore(afterNode, parent.nextSibling);
1635
+ const newRange = document.createRange();
1636
+ newRange.setStart(afterNode, 1);
1637
+ newRange.collapse(true);
1638
+ sel.removeAllRanges();
1639
+ sel.addRange(newRange);
1640
+ this.onEditorInput();
1641
+ return;
1642
+ }
1643
+ // Sub-case B: Caret is in normal text right after typing #hashtag
1644
+ const textBefore = (node.textContent || '').substring(0, offset);
1645
+ const match = textBefore.match(/(?:^|[\s\u00A0])(#([a-zA-Z0-9_\u0080-\uFFFF]+))$/);
1646
+ if (!match)
1647
+ return;
1648
+ event.preventDefault();
1649
+ const fullTag = match[1];
1650
+ const tagStartIndex = offset - fullTag.length;
1651
+ const tagRange = document.createRange();
1652
+ tagRange.setStart(node, tagStartIndex);
1653
+ tagRange.setEnd(node, offset);
1654
+ tagRange.deleteContents();
1655
+ const span = document.createElement('span');
1656
+ span.className = 'ngx-editor-hashtag post-content-hashtag';
1657
+ span.textContent = fullTag;
1658
+ tagRange.insertNode(span);
1659
+ const afterNode = document.createTextNode('\u00A0');
1660
+ span.parentNode?.insertBefore(afterNode, span.nextSibling);
1661
+ const newRange = document.createRange();
1662
+ newRange.setStart(afterNode, 1);
1663
+ newRange.collapse(true);
1664
+ sel.removeAllRanges();
1665
+ sel.addRange(newRange);
1666
+ this.onEditorInput();
1667
+ }
1668
+ }
1669
+ focus() {
1670
+ this.focusEditor();
1671
+ }
1672
+ getHtml() {
1673
+ return this.content;
1674
+ }
1675
+ getText() {
1676
+ return this.editorContentRef?.nativeElement?.innerText || '';
1677
+ }
1678
+ isFormatActive(format) {
1679
+ const key = format;
1680
+ if (this.activeStates[key] !== undefined) {
1681
+ return this.activeStates[key];
1682
+ }
1683
+ return this.commandService.queryState(format);
1684
+ }
1685
+ /* =========================================================
1686
+ * INTERNAL HANDLERS & MODALS
1687
+ * ========================================================= */
1688
+ onFormatBlockChange(event) {
1689
+ const target = event.target;
1690
+ this.formatBlock(target.value);
1691
+ }
1692
+ onFontNameChange(event) {
1693
+ const target = event.target;
1694
+ this.setFontFamily(target.value);
1695
+ }
1696
+ onFontSizeChange(event) {
1697
+ const target = event.target;
1698
+ this.setFontSize(target.value);
1699
+ }
1700
+ wrapSelectionWithCode() {
1701
+ if (this.disabled || this.readonly || this.isSourceView)
1702
+ return;
1703
+ this.focusEditor();
1704
+ const sel = window.getSelection();
1705
+ if (!sel || sel.rangeCount === 0)
1706
+ return;
1707
+ const range = sel.getRangeAt(0);
1708
+ const selectedText = range.toString();
1709
+ if (!selectedText) {
1710
+ this.commandService.insertHtml('<code>&nbsp;</code>');
1711
+ }
1712
+ else {
1713
+ let node = range.commonAncestorContainer;
1714
+ let isInCode = false;
1715
+ while (node && node !== this.editorContentRef.nativeElement) {
1716
+ if (node.nodeName.toLowerCase() === 'code') {
1717
+ isInCode = true;
1718
+ break;
1719
+ }
1720
+ node = node.parentNode;
1721
+ }
1722
+ if (isInCode) {
1723
+ this.commandService.clearFormatting();
1724
+ }
1725
+ else {
1726
+ this.commandService.insertHtml(`<code>${selectedText}</code>`);
1727
+ }
1728
+ }
1729
+ this.onEditorInput();
1730
+ this.updateEditorState();
1731
+ }
1732
+ toggleColorPicker(type) {
1733
+ if (this.disabled || this.readonly || this.isSourceView)
1734
+ return;
1735
+ this.savedSelectionRange = this.commandService.saveSelection();
1736
+ if (this.activeColorPicker === type) {
1737
+ this.activeColorPicker = null;
1738
+ }
1739
+ else {
1740
+ this.activeColorPicker = type;
1741
+ }
1742
+ }
1743
+ applyColor(color) {
1744
+ this.commandService.restoreSelection(this.savedSelectionRange);
1745
+ if (this.activeColorPicker === 'text') {
1746
+ this.selectedTextColor = color;
1747
+ this.commandService.setTextColor(color);
1748
+ }
1749
+ else if (this.activeColorPicker === 'background') {
1750
+ this.selectedBgColor = color;
1751
+ this.commandService.setBackgroundColor(color);
1752
+ }
1753
+ this.activeColorPicker = null;
1754
+ this.onEditorInput();
1755
+ }
1756
+ onCustomColorChange(event) {
1757
+ const input = event.target;
1758
+ this.applyColor(input.value);
1759
+ }
1760
+ openLinkModal() {
1761
+ if (this.disabled || this.readonly || this.isSourceView)
1762
+ return;
1763
+ this.savedSelectionRange = this.commandService.saveSelection();
1764
+ const selectedText = window.getSelection()?.toString() || '';
1765
+ this.linkModel = {
1766
+ text: selectedText,
1767
+ url: '',
1768
+ newTab: true,
1769
+ };
1770
+ this.showLinkModal = true;
1771
+ }
1772
+ closeLinkModal() {
1773
+ this.showLinkModal = false;
1774
+ }
1775
+ insertLinkFromModal() {
1776
+ if (!this.linkModel.url)
1777
+ return;
1778
+ this.commandService.restoreSelection(this.savedSelectionRange);
1779
+ this.commandService.createLink(this.linkModel.url, this.linkModel.text, this.linkModel.newTab);
1780
+ this.showLinkModal = false;
1781
+ this.onEditorInput();
1782
+ this.updateEditorState();
1783
+ }
1784
+ openImageModal() {
1785
+ if (this.disabled || this.readonly || this.isSourceView)
1786
+ return;
1787
+ this.savedSelectionRange = this.commandService.saveSelection();
1788
+ this.imageModel = { url: '', alt: '' };
1789
+ this.showImageModal = true;
1790
+ }
1791
+ closeImageModal() {
1792
+ this.showImageModal = false;
1793
+ }
1794
+ onImageFileUpload(event) {
1795
+ const input = event.target;
1796
+ if (input.files && input.files.length > 0) {
1797
+ const file = input.files[0];
1798
+ const reader = new FileReader();
1799
+ reader.onload = () => {
1800
+ this.imageModel.url = reader.result;
1801
+ };
1802
+ reader.readAsDataURL(file);
1803
+ }
1804
+ }
1805
+ insertImageFromModal() {
1806
+ if (!this.imageModel.url)
1807
+ return;
1808
+ this.commandService.restoreSelection(this.savedSelectionRange);
1809
+ this.commandService.insertImage(this.imageModel.url, this.imageModel.alt);
1810
+ this.showImageModal = false;
1811
+ this.onEditorInput();
1812
+ }
1813
+ toggleSourceView() {
1814
+ this.isSourceView = !this.isSourceView;
1815
+ if (!this.isSourceView) {
1816
+ setTimeout(() => {
1817
+ if (this.editorContentRef) {
1818
+ this.editorContentRef.nativeElement.innerHTML = this.content;
1819
+ this.focusEditor();
1820
+ }
1821
+ });
1822
+ }
1823
+ }
1824
+ updateEditorState() {
1825
+ this.activeStates = {
1826
+ bold: this.commandService.queryState('bold'),
1827
+ italic: this.commandService.queryState('italic'),
1828
+ underline: this.commandService.queryState('underline'),
1829
+ strikeThrough: this.commandService.queryState('strikeThrough'),
1830
+ subscript: this.commandService.queryState('subscript'),
1831
+ superscript: this.commandService.queryState('superscript'),
1832
+ justifyLeft: this.commandService.queryState('justifyLeft'),
1833
+ justifyCenter: this.commandService.queryState('justifyCenter'),
1834
+ justifyRight: this.commandService.queryState('justifyRight'),
1835
+ justifyFull: this.commandService.queryState('justifyFull'),
1836
+ insertUnorderedList: this.commandService.queryState('insertUnorderedList'),
1837
+ insertOrderedList: this.commandService.queryState('insertOrderedList'),
1838
+ link: this.isCursorInsideTag('a'),
1839
+ };
1840
+ this.activeBlockTag = this.getCurrentBlockTag();
1841
+ this.cdr.markForCheck();
1842
+ }
1843
+ isCursorInsideTag(tagName) {
1844
+ const sel = window.getSelection();
1845
+ if (!sel || !sel.anchorNode)
1846
+ return false;
1847
+ let node = sel.anchorNode;
1848
+ while (node && node !== this.editorContentRef?.nativeElement) {
1849
+ if (node.nodeName.toLowerCase() === tagName.toLowerCase()) {
1850
+ return true;
1851
+ }
1852
+ node = node.parentNode;
1853
+ }
1854
+ return false;
1855
+ }
1856
+ getCurrentBlockTag() {
1857
+ const blockTags = ['h1', 'h2', 'h3', 'h4', 'blockquote', 'pre', 'p'];
1858
+ const sel = window.getSelection();
1859
+ if (!sel || !sel.anchorNode)
1860
+ return 'p';
1861
+ let node = sel.anchorNode;
1862
+ while (node && node !== this.editorContentRef?.nativeElement) {
1863
+ const name = node.nodeName.toLowerCase();
1864
+ if (blockTags.includes(name)) {
1865
+ return name;
1866
+ }
1867
+ node = node.parentNode;
1868
+ }
1869
+ return 'p';
1870
+ }
1871
+ updateStats() {
1872
+ this.stats = this.commandService.countStats(this.content);
1873
+ }
1874
+ getEditorElement() {
1875
+ return this.editorContentRef?.nativeElement ?? null;
1876
+ }
1877
+ focusEditor() {
1878
+ if (this.editorContentRef && this.editorContentRef.nativeElement) {
1879
+ this.editorContentRef.nativeElement.focus();
1880
+ }
1881
+ }
1882
+ onDocumentClick(event) {
1883
+ if (this.activeColorPicker && !this.hostRef.nativeElement.contains(event.target)) {
1884
+ this.activeColorPicker = null;
1885
+ }
1886
+ }
1887
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NgxRichTextEditorComponent, deps: [{ token: EditorCommandService }, { token: i2.DomSanitizer }, { token: i0.ElementRef }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1888
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.22", type: NgxRichTextEditorComponent, isStandalone: true, selector: "ngx-rich-text-editor", inputs: { value: "value", placeholder: "placeholder", disabled: "disabled", readonly: "readonly", showToolbar: "showToolbar", showStatusBar: "showStatusBar", height: "height", minHeight: "minHeight", maxHeight: "maxHeight", tools: "tools", maxCharacters: "maxCharacters", fontFamily: "fontFamily", fontSize: "fontSize", fontFamilies: "fontFamilies", customClass: "customClass", editorStyle: "editorStyle", contentStyle: "contentStyle", hashtagColor: "hashtagColor", highlightHashtags: "highlightHashtags", config: "config" }, outputs: { valueChange: "valueChange", textChange: "textChange", editorFocus: "editorFocus", editorBlur: "editorBlur" }, host: { listeners: { "document:click": "onDocumentClick($event)" } }, providers: [
1889
+ {
1890
+ provide: NG_VALUE_ACCESSOR,
1891
+ useExisting: forwardRef(() => NgxRichTextEditorComponent),
1892
+ multi: true,
1893
+ },
1894
+ ], viewQueries: [{ propertyName: "editorContentRef", first: true, predicate: ["editorContent"], descendants: true }, { propertyName: "sourceTextareaRef", first: true, predicate: ["sourceTextarea"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div\n class=\"ngx-editor-container\"\n [ngClass]=\"customClass\"\n [class.is-focused]=\"isFocused\"\n [class.is-disabled]=\"disabled\"\n [style.min-height]=\"computedConfig.minHeight\"\n [style.max-height]=\"computedConfig.maxHeight\"\n [style.font-family]=\"fontFamily || computedConfig.fontFamily || null\"\n [style.font-size]=\"fontSize || computedConfig.fontSize || null\"\n [ngStyle]=\"parsedEditorStyle\"\n>\n <!-- Toolbar -->\n @if (computedConfig.showToolbar && !readonly) {\n <!-- Array-configured Toolbar (if tools input is provided) -->\n @if (computedConfig.tools && computedConfig.tools.length > 0) {\n <ngx-editor-toolbar\n [editor]=\"this\"\n [tools]=\"computedConfig.tools\"\n [fontFamilies]=\"resolvedFontFamilies\"\n ></ngx-editor-toolbar>\n } @else {\n <!-- Default Full Toolbar -->\n <div class=\"ngx-editor-toolbar\" role=\"toolbar\" aria-label=\"Editor Toolbar\">\n <!-- History -->\n @if (computedConfig.toolbar?.history) {\n <div class=\"ngx-editor-toolbar-group\">\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Undo (Ctrl+Z)\"\n aria-label=\"Undo\"\n (mousedown)=\"$event.preventDefault(); undo()\"\n >\n <ngx-editor-icon name=\"undo\"></ngx-editor-icon>\n </button>\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Redo (Ctrl+Y)\"\n aria-label=\"Redo\"\n (mousedown)=\"$event.preventDefault(); redo()\"\n >\n <ngx-editor-icon name=\"redo\"></ngx-editor-icon>\n </button>\n </div>\n }\n\n <!-- Format Block -->\n @if (computedConfig.toolbar?.format) {\n <div class=\"ngx-editor-toolbar-group\">\n <select\n class=\"ngx-editor-select\"\n title=\"Paragraph Format\"\n aria-label=\"Paragraph Format\"\n [value]=\"activeBlockTag\"\n (change)=\"onFormatBlockChange($event)\"\n >\n <option value=\"p\">Normal text</option>\n <option value=\"h1\">Heading 1</option>\n <option value=\"h2\">Heading 2</option>\n <option value=\"h3\">Heading 3</option>\n <option value=\"h4\">Heading 4</option>\n <option value=\"blockquote\">Quote</option>\n <option value=\"pre\">Code Block</option>\n </select>\n </div>\n }\n\n <!-- Font Family -->\n @if (computedConfig.toolbar?.fontName) {\n <div class=\"ngx-editor-toolbar-group\">\n <select\n class=\"ngx-editor-select\"\n title=\"Font Family\"\n aria-label=\"Font Family\"\n (change)=\"onFontNameChange($event)\"\n >\n <option value=\"\">Font</option>\n @for (font of resolvedFontFamilies; track font.value) {\n <option [value]=\"font.value\">{{ font.label }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Font Size -->\n @if (computedConfig.toolbar?.fontSize) {\n <div class=\"ngx-editor-toolbar-group\">\n <select\n class=\"ngx-editor-select\"\n title=\"Font Size\"\n aria-label=\"Font Size\"\n (change)=\"onFontSizeChange($event)\"\n >\n <option value=\"\">Size</option>\n <option value=\"1\">Small (10pt)</option>\n <option value=\"3\">Normal (12pt)</option>\n <option value=\"5\">Large (18pt)</option>\n <option value=\"7\">Huge (36pt)</option>\n </select>\n </div>\n }\n\n <!-- Inline Styles: Bold, Italic, Underline, Strike, Sub, Sup, Code -->\n @if (computedConfig.toolbar?.inline) {\n <div class=\"ngx-editor-toolbar-group\">\n @if (computedConfig.toolbar?.inline?.bold !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.bold\"\n title=\"Bold (Ctrl+B)\"\n aria-label=\"Bold\"\n (mousedown)=\"$event.preventDefault(); bold()\"\n >\n <ngx-editor-icon name=\"bold\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.italic !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.italic\"\n title=\"Italic (Ctrl+I)\"\n aria-label=\"Italic\"\n (mousedown)=\"$event.preventDefault(); italic()\"\n >\n <ngx-editor-icon name=\"italic\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.underline !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.underline\"\n title=\"Underline (Ctrl+U)\"\n aria-label=\"Underline\"\n (mousedown)=\"$event.preventDefault(); underline()\"\n >\n <ngx-editor-icon name=\"underline\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.strikeThrough !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.strikeThrough\"\n title=\"Strikethrough\"\n aria-label=\"Strikethrough\"\n (mousedown)=\"$event.preventDefault(); strike()\"\n >\n <ngx-editor-icon name=\"strike\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.subscript !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.subscript\"\n title=\"Subscript\"\n aria-label=\"Subscript\"\n (mousedown)=\"$event.preventDefault(); subscript()\"\n >\n <ngx-editor-icon name=\"subscript\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.superscript !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.superscript\"\n title=\"Superscript\"\n aria-label=\"Superscript\"\n (mousedown)=\"$event.preventDefault(); superscript()\"\n >\n <ngx-editor-icon name=\"superscript\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.code !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Inline Code\"\n aria-label=\"Inline Code\"\n (mousedown)=\"$event.preventDefault(); wrapSelectionWithCode()\"\n >\n <ngx-editor-icon name=\"code\"></ngx-editor-icon>\n </button>\n }\n </div>\n }\n\n <!-- Colors -->\n @if (computedConfig.toolbar?.color) {\n <div class=\"ngx-editor-toolbar-group\" style=\"position: relative;\">\n @if (computedConfig.toolbar?.color?.textColor !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn ngx-editor-color-btn\"\n title=\"Text Color\"\n aria-label=\"Text Color\"\n (click)=\"toggleColorPicker('text')\"\n >\n <ngx-editor-icon name=\"textColor\"></ngx-editor-icon>\n </button>\n <div\n class=\"color-bar\"\n [style.background-color]=\"selectedTextColor\"\n ></div>\n }\n\n @if (computedConfig.toolbar?.color?.backgroundColor !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn ngx-editor-color-btn\"\n title=\"Highlight Color\"\n aria-label=\"Highlight Color\"\n (click)=\"toggleColorPicker('background')\"\n >\n <ngx-editor-icon name=\"bgColor\"></ngx-editor-icon>\n </button>\n <div\n class=\"color-bar\"\n [style.background-color]=\"selectedBgColor\"\n ></div>\n }\n\n <!-- Color Popover -->\n @if (activeColorPicker) {\n <div class=\"ngx-color-popover\" (mousedown)=\"$event.stopPropagation()\">\n <div class=\"color-grid\">\n @for (color of computedConfig.colorPalette; track color) {\n <button\n type=\"button\"\n class=\"color-swatch\"\n [style.background-color]=\"color\"\n (click)=\"applyColor(color)\"\n ></button>\n }\n </div>\n <div class=\"custom-color-picker\">\n <input\n type=\"color\"\n (change)=\"onCustomColorChange($event)\"\n />\n <span>Custom color</span>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Alignment -->\n @if (computedConfig.toolbar?.alignment) {\n <div class=\"ngx-editor-toolbar-group\">\n @if (computedConfig.toolbar?.alignment?.left !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.justifyLeft\"\n title=\"Align Left\"\n aria-label=\"Align Left\"\n (mousedown)=\"$event.preventDefault(); alignLeft()\"\n >\n <ngx-editor-icon name=\"alignLeft\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.alignment?.center !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.justifyCenter\"\n title=\"Align Center\"\n aria-label=\"Align Center\"\n (mousedown)=\"$event.preventDefault(); alignCenter()\"\n >\n <ngx-editor-icon name=\"alignCenter\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.alignment?.right !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.justifyRight\"\n title=\"Align Right\"\n aria-label=\"Align Right\"\n (mousedown)=\"$event.preventDefault(); alignRight()\"\n >\n <ngx-editor-icon name=\"alignRight\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.alignment?.justify !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.justifyFull\"\n title=\"Justify\"\n aria-label=\"Justify\"\n (mousedown)=\"$event.preventDefault(); alignJustify()\"\n >\n <ngx-editor-icon name=\"alignJustify\"></ngx-editor-icon>\n </button>\n }\n </div>\n }\n\n <!-- Lists & Indentation -->\n @if (computedConfig.toolbar?.list) {\n <div class=\"ngx-editor-toolbar-group\">\n @if (computedConfig.toolbar?.list?.unordered !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.insertUnorderedList\"\n title=\"Bullet List\"\n aria-label=\"Bullet List\"\n (mousedown)=\"$event.preventDefault(); insertUnorderedList()\"\n >\n <ngx-editor-icon name=\"unorderedList\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.list?.ordered !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.insertOrderedList\"\n title=\"Numbered List\"\n aria-label=\"Numbered List\"\n (mousedown)=\"$event.preventDefault(); insertOrderedList()\"\n >\n <ngx-editor-icon name=\"orderedList\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.list?.indent !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Increase Indent\"\n aria-label=\"Increase Indent\"\n (mousedown)=\"$event.preventDefault(); indent()\"\n >\n <ngx-editor-icon name=\"indent\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.list?.outdent !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Decrease Indent\"\n aria-label=\"Decrease Indent\"\n (mousedown)=\"$event.preventDefault(); outdent()\"\n >\n <ngx-editor-icon name=\"outdent\"></ngx-editor-icon>\n </button>\n }\n </div>\n }\n\n <!-- Insert Options -->\n @if (computedConfig.toolbar?.insert) {\n <div class=\"ngx-editor-toolbar-group\">\n @if (computedConfig.toolbar?.insert?.link !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.link\"\n title=\"Insert Link (Ctrl+K)\"\n aria-label=\"Insert Link\"\n (click)=\"openLinkModal()\"\n >\n <ngx-editor-icon name=\"link\"></ngx-editor-icon>\n </button>\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Remove Link\"\n aria-label=\"Remove Link\"\n (mousedown)=\"$event.preventDefault(); unlink()\"\n >\n <ngx-editor-icon name=\"unlink\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.insert?.image !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Insert Image\"\n aria-label=\"Insert Image\"\n (click)=\"openImageModal()\"\n >\n <ngx-editor-icon name=\"image\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.insert?.horizontalRule !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Horizontal Line\"\n aria-label=\"Horizontal Line\"\n (mousedown)=\"$event.preventDefault(); insertHorizontalRule()\"\n >\n <ngx-editor-icon name=\"horizontalRule\"></ngx-editor-icon>\n </button>\n }\n </div>\n }\n\n <!-- Extra Actions -->\n @if (computedConfig.toolbar?.extra) {\n <div class=\"ngx-editor-toolbar-group\">\n @if (computedConfig.toolbar?.extra?.clearFormat !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Clear Formatting\"\n aria-label=\"Clear Formatting\"\n (mousedown)=\"$event.preventDefault(); clearFormatting()\"\n >\n <ngx-editor-icon name=\"clearFormat\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.extra?.sourceView !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isSourceView\"\n title=\"View HTML Source\"\n aria-label=\"View HTML Source\"\n (click)=\"toggleSourceView()\"\n >\n <ngx-editor-icon name=\"sourceView\"></ngx-editor-icon>\n </button>\n }\n </div>\n }\n </div>\n }\n }\n\n <!-- Editor Content Area -->\n <div class=\"ngx-editor-content-wrapper\">\n <!-- Visual contenteditable -->\n <div\n #editorContent\n class=\"ngx-editor-content\"\n [style.display]=\"isSourceView ? 'none' : 'block'\"\n [style.height]=\"computedConfig.height\"\n [style.font-family]=\"fontFamily || computedConfig.fontFamily || null\"\n [style.font-size]=\"fontSize || computedConfig.fontSize || null\"\n [ngStyle]=\"parsedContentStyle\"\n [attr.contenteditable]=\"!disabled && !readonly\"\n [attr.spellcheck]=\"computedConfig.spellcheck\"\n [attr.data-placeholder]=\"computedConfig.placeholder\"\n [attr.data-empty]=\"isEmpty\"\n (input)=\"onEditorInput()\"\n (focus)=\"onEditorFocus($event)\"\n (blur)=\"onEditorBlur($event)\"\n (keydown)=\"onEditorKeydown($event)\"\n (keyup)=\"updateEditorState()\"\n (mouseup)=\"updateEditorState()\"\n (paste)=\"onEditorPaste()\"\n ></div>\n\n <!-- Raw HTML Source Code View -->\n @if (isSourceView) {\n <textarea\n #sourceTextarea\n class=\"ngx-editor-source\"\n [style.height]=\"computedConfig.height\"\n [disabled]=\"disabled || readonly\"\n [value]=\"content\"\n (input)=\"onSourceInput($event)\"\n ></textarea>\n }\n </div>\n\n <!-- Status Bar -->\n @if (computedConfig.showStatusBar) {\n <div class=\"ngx-editor-statusbar\">\n <div class=\"statusbar-left\">\n @if (activeBlockTag) {\n <span class=\"status-tag\">&lt;{{ activeBlockTag }}&gt;</span>\n }\n @if (isSourceView) {\n <span class=\"status-badge\">HTML SOURCE MODE</span>\n }\n </div>\n <div class=\"statusbar-right\">\n @if (computedConfig.showWordCount) {\n <span>Words: <strong>{{ stats.words }}</strong></span>\n }\n @if (computedConfig.maxCharacters) {\n <span [class.limit-reached]=\"stats.chars > computedConfig.maxCharacters\">\n {{ stats.chars }}/{{ computedConfig.maxCharacters }} Characters\n </span>\n } @else if (computedConfig.showCharCount) {\n <span>Characters: <strong>{{ stats.chars }}</strong></span>\n }\n </div>\n </div>\n }\n\n <!-- Link Modal -->\n @if (showLinkModal) {\n <div class=\"ngx-editor-modal-backdrop\" (click)=\"closeLinkModal()\">\n <div class=\"ngx-editor-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Insert Link</h3>\n <button type=\"button\" class=\"ngx-editor-btn\" (click)=\"closeLinkModal()\" aria-label=\"Close\">\n <ngx-editor-icon name=\"close\"></ngx-editor-icon>\n </button>\n </div>\n <div class=\"modal-body\">\n <div class=\"form-group\">\n <label>Link Text</label>\n <input\n type=\"text\"\n placeholder=\"Display text\"\n [(ngModel)]=\"linkModel.text\"\n />\n </div>\n <div class=\"form-group\">\n <label>Target URL</label>\n <input\n type=\"url\"\n placeholder=\"https://example.com\"\n [(ngModel)]=\"linkModel.url\"\n (keydown.enter)=\"insertLinkFromModal()\"\n />\n </div>\n <div class=\"form-group\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"linkModel.newTab\" />\n Open in a new tab\n </label>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn-secondary\" (click)=\"closeLinkModal()\">Cancel</button>\n <button\n type=\"button\"\n class=\"btn-primary\"\n [disabled]=\"!linkModel.url\"\n (click)=\"insertLinkFromModal()\"\n >\n Insert Link\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Image Modal -->\n @if (showImageModal) {\n <div class=\"ngx-editor-modal-backdrop\" (click)=\"closeImageModal()\">\n <div class=\"ngx-editor-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Insert Image</h3>\n <button type=\"button\" class=\"ngx-editor-btn\" (click)=\"closeImageModal()\" aria-label=\"Close\">\n <ngx-editor-icon name=\"close\"></ngx-editor-icon>\n </button>\n </div>\n <div class=\"modal-body\">\n <div class=\"form-group\">\n <label>Image URL</label>\n <input\n type=\"url\"\n placeholder=\"https://example.com/image.png\"\n [(ngModel)]=\"imageModel.url\"\n />\n </div>\n <div class=\"form-group\">\n <label>Or Upload File</label>\n <input\n type=\"file\"\n accept=\"image/*\"\n (change)=\"onImageFileUpload($event)\"\n />\n </div>\n <div class=\"form-group\">\n <label>Alt Description</label>\n <input\n type=\"text\"\n placeholder=\"Image description\"\n [(ngModel)]=\"imageModel.alt\"\n />\n </div>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn-secondary\" (click)=\"closeImageModal()\">Cancel</button>\n <button\n type=\"button\"\n class=\"btn-primary\"\n [disabled]=\"!imageModel.url\"\n (click)=\"insertImageFromModal()\"\n >\n Insert Image\n </button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:block;font-family:var(--ngx-editor-font-family, inherit);box-sizing:border-box;--ngx-editor-font-family: inherit;--ngx-editor-font-size: 15px;--ngx-editor-line-height: 1.65;--ngx-editor-border: #e2e8f0;--ngx-editor-border-focus: #3b82f6;--ngx-editor-bg: #ffffff;--ngx-editor-toolbar-bg: #f8fafc;--ngx-editor-toolbar-btn-hover: #e2e8f0;--ngx-editor-toolbar-btn-active: #dbeafe;--ngx-editor-toolbar-btn-active-color: #1d4ed8;--ngx-editor-text: #1e293b;--ngx-editor-muted: #64748b;--ngx-editor-radius: 8px;--ngx-editor-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);--ngx-editor-shadow-focus: 0 0 0 3px rgba(59, 130, 246, .25);--ngx-editor-hashtag-color: #0069FF}.ngx-editor-container{display:flex;flex-direction:column;background-color:var(--ngx-editor-bg);border:1px solid var(--ngx-editor-border);border-radius:var(--ngx-editor-radius);box-shadow:var(--ngx-editor-shadow);overflow:hidden;transition:border-color .2s ease,box-shadow .2s ease;position:relative}.ngx-editor-container.is-focused{border-color:var(--ngx-editor-border-focus);box-shadow:var(--ngx-editor-shadow-focus)}.ngx-editor-container.is-disabled{background-color:#f1f5f9;opacity:.75;pointer-events:none;-webkit-user-select:none;user-select:none}.ngx-editor-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:6px 8px;background-color:var(--ngx-editor-toolbar-bg);border-bottom:1px solid var(--ngx-editor-border);-webkit-user-select:none;user-select:none;z-index:10}.ngx-editor-toolbar-group{display:flex;align-items:center;gap:2px}.ngx-editor-toolbar-group:not(:last-child):after{content:\"\";display:inline-block;width:1px;height:18px;background-color:var(--ngx-editor-border);margin:0 4px}.ngx-editor-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--ngx-editor-text);cursor:pointer;transition:background-color .15s ease,color .15s ease,border-color .15s ease;outline:none}.ngx-editor-btn:hover:not(:disabled){background-color:var(--ngx-editor-toolbar-btn-hover)}.ngx-editor-btn:active:not(:disabled),.ngx-editor-btn.is-active{background-color:var(--ngx-editor-toolbar-btn-active);color:var(--ngx-editor-toolbar-btn-active-color);border-color:#bfdbfe}.ngx-editor-btn:disabled{opacity:.35;cursor:not-allowed}.ngx-editor-btn svg{display:block;pointer-events:none}.ngx-editor-select{height:32px;padding:0 8px;font-size:13px;color:var(--ngx-editor-text);background-color:var(--ngx-editor-bg);border:1px solid var(--ngx-editor-border);border-radius:4px;outline:none;cursor:pointer;transition:border-color .15s ease}.ngx-editor-select:hover{border-color:#cbd5e1}.ngx-editor-select:focus{border-color:var(--ngx-editor-border-focus)}.ngx-editor-color-btn{position:relative}.ngx-editor-color-btn .color-bar{position:absolute;bottom:4px;left:6px;right:6px;height:3px;border-radius:1px}.ngx-color-popover{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid var(--ngx-editor-border);border-radius:6px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;padding:8px;z-index:50;width:176px}.ngx-color-popover .color-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:4px;margin-bottom:8px}.ngx-color-popover .color-swatch{width:22px;height:22px;border-radius:3px;border:1px solid rgba(0,0,0,.15);cursor:pointer;padding:0;transition:transform .1s ease}.ngx-color-popover .color-swatch:hover{transform:scale(1.15);z-index:2}.ngx-color-popover .custom-color-picker{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--ngx-editor-muted)}.ngx-color-popover .custom-color-picker input[type=color]{width:28px;height:24px;border:1px solid var(--ngx-editor-border);border-radius:3px;padding:0;cursor:pointer;background:none}.ngx-editor-content-wrapper{position:relative;flex:1;display:flex;flex-direction:column}.ngx-editor-content{flex:1;padding:16px;outline:none;font-family:var(--ngx-editor-font-family, inherit);font-size:var(--ngx-editor-font-size, 15px);line-height:var(--ngx-editor-line-height, 1.65);color:var(--ngx-editor-text);overflow-y:auto;min-height:200px;word-break:break-word}.ngx-editor-content p{margin:0 0 1em}.ngx-editor-content h1{font-size:2em;font-weight:700;margin:.67em 0;line-height:1.25}.ngx-editor-content h2{font-size:1.6em;font-weight:600;margin:.75em 0;line-height:1.3}.ngx-editor-content h3{font-size:1.3em;font-weight:600;margin:.83em 0}.ngx-editor-content h4{font-size:1.1em;font-weight:600;margin:1em 0}.ngx-editor-content ul,.ngx-editor-content ol{margin:0 0 1em 1.5em;padding:0}.ngx-editor-content li{margin-bottom:.25em}.ngx-editor-content blockquote{margin:1em 0;padding:.5em 1em;border-left:4px solid var(--ngx-editor-border-focus);background-color:#f8fafc;color:var(--ngx-editor-muted);font-style:italic}.ngx-editor-content pre{background-color:#1e293b;color:#f8fafc;padding:12px;border-radius:6px;font-family:Consolas,Monaco,Courier New,monospace;font-size:13px;overflow-x:auto;margin:1em 0}.ngx-editor-content code{background-color:#f1f5f9;color:#e11d48;padding:2px 5px;border-radius:4px;font-family:Consolas,Monaco,Courier New,monospace;font-size:.9em}.ngx-editor-content a{color:#2563eb;text-decoration:underline;text-underline-offset:2px;cursor:pointer}.ngx-editor-content .ngx-editor-hashtag,.ngx-editor-content .post-content-hashtag{color:var(--ngx-editor-hashtag-color, #0069FF)!important;font-weight:500;display:inline;cursor:text}.ngx-editor-content hr{border:none;border-top:1px solid var(--ngx-editor-border);margin:1.5em 0}.ngx-editor-content img{max-width:100%;height:auto;border-radius:4px}.ngx-editor-content[data-empty=true]:before{content:attr(data-placeholder);position:absolute;color:#94a3b8;pointer-events:none;-webkit-user-select:none;user-select:none}.ngx-editor-content:has(ol,ul,li,table,img,hr,blockquote,pre):before{display:none!important;content:none!important}.ngx-editor-source{width:100%;flex:1;min-height:200px;padding:16px;border:none;outline:none;font-family:Consolas,Monaco,Courier New,monospace;font-size:13.5px;line-height:1.5;background-color:#0f172a;color:#38bdf8;resize:vertical;box-sizing:border-box}.ngx-editor-statusbar{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;background-color:var(--ngx-editor-toolbar-bg);border-top:1px solid var(--ngx-editor-border);font-size:12px;color:var(--ngx-editor-muted)}.ngx-editor-statusbar .statusbar-left,.ngx-editor-statusbar .statusbar-right{display:flex;align-items:center;gap:12px}.ngx-editor-statusbar .status-tag{background-color:#e2e8f0;padding:2px 6px;border-radius:4px;font-weight:600;text-transform:uppercase;font-size:10px}.ngx-editor-statusbar .status-badge{background-color:#fee2e2;color:#ef4444;padding:2px 6px;border-radius:4px;font-weight:500;font-size:11px}.ngx-editor-modal-backdrop{position:absolute;inset:0;background:#0f172a66;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:100;animation:fadeIn .15s ease-out}.ngx-editor-modal{background:#fff;border-radius:8px;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;width:90%;max-width:400px;padding:20px;box-sizing:border-box}.ngx-editor-modal .modal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}.ngx-editor-modal .modal-header h3{margin:0;font-size:16px;font-weight:600;color:var(--ngx-editor-text)}.ngx-editor-modal .modal-body{display:flex;flex-direction:column;gap:12px}.ngx-editor-modal .form-group{display:flex;flex-direction:column;gap:4px}.ngx-editor-modal .form-group label{font-size:13px;font-weight:500;color:var(--ngx-editor-text)}.ngx-editor-modal .form-group input[type=text],.ngx-editor-modal .form-group input[type=url]{padding:8px 12px;border:1px solid var(--ngx-editor-border);border-radius:6px;font-size:14px;outline:none;transition:border-color .15s ease}.ngx-editor-modal .form-group input[type=text]:focus,.ngx-editor-modal .form-group input[type=url]:focus{border-color:var(--ngx-editor-border-focus)}.ngx-editor-modal .form-group .checkbox-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;color:var(--ngx-editor-text);-webkit-user-select:none;user-select:none}.ngx-editor-modal .modal-footer{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}.ngx-editor-modal .modal-footer button{padding:7px 14px;border-radius:6px;font-size:13px;font-weight:500;cursor:pointer;transition:all .15s ease;outline:none}.ngx-editor-modal .modal-footer .btn-secondary{background:#f1f5f9;border:1px solid var(--ngx-editor-border);color:var(--ngx-editor-text)}.ngx-editor-modal .modal-footer .btn-secondary:hover{background:#e2e8f0}.ngx-editor-modal .modal-footer .btn-primary{background:#2563eb;border:1px solid #2563eb;color:#fff}.ngx-editor-modal .modal-footer .btn-primary:hover{background:#1d4ed8}.ngx-editor-modal .modal-footer .btn-primary:disabled{opacity:.5;cursor:not-allowed}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NgxEditorIconComponent, selector: "ngx-editor-icon", inputs: ["name"] }, { kind: "component", type: NgxEditorToolbarComponent, selector: "ngx-editor-toolbar", inputs: ["editor", "tools", "fontFamilies"], outputs: ["actionTriggered"] }] });
1895
+ }
1896
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImport: i0, type: NgxRichTextEditorComponent, decorators: [{
1897
+ type: Component,
1898
+ args: [{ selector: 'ngx-rich-text-editor', standalone: true, imports: [CommonModule, FormsModule, NgxEditorIconComponent, NgxEditorToolbarComponent], providers: [
1899
+ {
1900
+ provide: NG_VALUE_ACCESSOR,
1901
+ useExisting: forwardRef(() => NgxRichTextEditorComponent),
1902
+ multi: true,
1903
+ },
1904
+ ], template: "<div\n class=\"ngx-editor-container\"\n [ngClass]=\"customClass\"\n [class.is-focused]=\"isFocused\"\n [class.is-disabled]=\"disabled\"\n [style.min-height]=\"computedConfig.minHeight\"\n [style.max-height]=\"computedConfig.maxHeight\"\n [style.font-family]=\"fontFamily || computedConfig.fontFamily || null\"\n [style.font-size]=\"fontSize || computedConfig.fontSize || null\"\n [ngStyle]=\"parsedEditorStyle\"\n>\n <!-- Toolbar -->\n @if (computedConfig.showToolbar && !readonly) {\n <!-- Array-configured Toolbar (if tools input is provided) -->\n @if (computedConfig.tools && computedConfig.tools.length > 0) {\n <ngx-editor-toolbar\n [editor]=\"this\"\n [tools]=\"computedConfig.tools\"\n [fontFamilies]=\"resolvedFontFamilies\"\n ></ngx-editor-toolbar>\n } @else {\n <!-- Default Full Toolbar -->\n <div class=\"ngx-editor-toolbar\" role=\"toolbar\" aria-label=\"Editor Toolbar\">\n <!-- History -->\n @if (computedConfig.toolbar?.history) {\n <div class=\"ngx-editor-toolbar-group\">\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Undo (Ctrl+Z)\"\n aria-label=\"Undo\"\n (mousedown)=\"$event.preventDefault(); undo()\"\n >\n <ngx-editor-icon name=\"undo\"></ngx-editor-icon>\n </button>\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Redo (Ctrl+Y)\"\n aria-label=\"Redo\"\n (mousedown)=\"$event.preventDefault(); redo()\"\n >\n <ngx-editor-icon name=\"redo\"></ngx-editor-icon>\n </button>\n </div>\n }\n\n <!-- Format Block -->\n @if (computedConfig.toolbar?.format) {\n <div class=\"ngx-editor-toolbar-group\">\n <select\n class=\"ngx-editor-select\"\n title=\"Paragraph Format\"\n aria-label=\"Paragraph Format\"\n [value]=\"activeBlockTag\"\n (change)=\"onFormatBlockChange($event)\"\n >\n <option value=\"p\">Normal text</option>\n <option value=\"h1\">Heading 1</option>\n <option value=\"h2\">Heading 2</option>\n <option value=\"h3\">Heading 3</option>\n <option value=\"h4\">Heading 4</option>\n <option value=\"blockquote\">Quote</option>\n <option value=\"pre\">Code Block</option>\n </select>\n </div>\n }\n\n <!-- Font Family -->\n @if (computedConfig.toolbar?.fontName) {\n <div class=\"ngx-editor-toolbar-group\">\n <select\n class=\"ngx-editor-select\"\n title=\"Font Family\"\n aria-label=\"Font Family\"\n (change)=\"onFontNameChange($event)\"\n >\n <option value=\"\">Font</option>\n @for (font of resolvedFontFamilies; track font.value) {\n <option [value]=\"font.value\">{{ font.label }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Font Size -->\n @if (computedConfig.toolbar?.fontSize) {\n <div class=\"ngx-editor-toolbar-group\">\n <select\n class=\"ngx-editor-select\"\n title=\"Font Size\"\n aria-label=\"Font Size\"\n (change)=\"onFontSizeChange($event)\"\n >\n <option value=\"\">Size</option>\n <option value=\"1\">Small (10pt)</option>\n <option value=\"3\">Normal (12pt)</option>\n <option value=\"5\">Large (18pt)</option>\n <option value=\"7\">Huge (36pt)</option>\n </select>\n </div>\n }\n\n <!-- Inline Styles: Bold, Italic, Underline, Strike, Sub, Sup, Code -->\n @if (computedConfig.toolbar?.inline) {\n <div class=\"ngx-editor-toolbar-group\">\n @if (computedConfig.toolbar?.inline?.bold !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.bold\"\n title=\"Bold (Ctrl+B)\"\n aria-label=\"Bold\"\n (mousedown)=\"$event.preventDefault(); bold()\"\n >\n <ngx-editor-icon name=\"bold\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.italic !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.italic\"\n title=\"Italic (Ctrl+I)\"\n aria-label=\"Italic\"\n (mousedown)=\"$event.preventDefault(); italic()\"\n >\n <ngx-editor-icon name=\"italic\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.underline !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.underline\"\n title=\"Underline (Ctrl+U)\"\n aria-label=\"Underline\"\n (mousedown)=\"$event.preventDefault(); underline()\"\n >\n <ngx-editor-icon name=\"underline\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.strikeThrough !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.strikeThrough\"\n title=\"Strikethrough\"\n aria-label=\"Strikethrough\"\n (mousedown)=\"$event.preventDefault(); strike()\"\n >\n <ngx-editor-icon name=\"strike\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.subscript !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.subscript\"\n title=\"Subscript\"\n aria-label=\"Subscript\"\n (mousedown)=\"$event.preventDefault(); subscript()\"\n >\n <ngx-editor-icon name=\"subscript\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.superscript !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.superscript\"\n title=\"Superscript\"\n aria-label=\"Superscript\"\n (mousedown)=\"$event.preventDefault(); superscript()\"\n >\n <ngx-editor-icon name=\"superscript\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.inline?.code !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Inline Code\"\n aria-label=\"Inline Code\"\n (mousedown)=\"$event.preventDefault(); wrapSelectionWithCode()\"\n >\n <ngx-editor-icon name=\"code\"></ngx-editor-icon>\n </button>\n }\n </div>\n }\n\n <!-- Colors -->\n @if (computedConfig.toolbar?.color) {\n <div class=\"ngx-editor-toolbar-group\" style=\"position: relative;\">\n @if (computedConfig.toolbar?.color?.textColor !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn ngx-editor-color-btn\"\n title=\"Text Color\"\n aria-label=\"Text Color\"\n (click)=\"toggleColorPicker('text')\"\n >\n <ngx-editor-icon name=\"textColor\"></ngx-editor-icon>\n </button>\n <div\n class=\"color-bar\"\n [style.background-color]=\"selectedTextColor\"\n ></div>\n }\n\n @if (computedConfig.toolbar?.color?.backgroundColor !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn ngx-editor-color-btn\"\n title=\"Highlight Color\"\n aria-label=\"Highlight Color\"\n (click)=\"toggleColorPicker('background')\"\n >\n <ngx-editor-icon name=\"bgColor\"></ngx-editor-icon>\n </button>\n <div\n class=\"color-bar\"\n [style.background-color]=\"selectedBgColor\"\n ></div>\n }\n\n <!-- Color Popover -->\n @if (activeColorPicker) {\n <div class=\"ngx-color-popover\" (mousedown)=\"$event.stopPropagation()\">\n <div class=\"color-grid\">\n @for (color of computedConfig.colorPalette; track color) {\n <button\n type=\"button\"\n class=\"color-swatch\"\n [style.background-color]=\"color\"\n (click)=\"applyColor(color)\"\n ></button>\n }\n </div>\n <div class=\"custom-color-picker\">\n <input\n type=\"color\"\n (change)=\"onCustomColorChange($event)\"\n />\n <span>Custom color</span>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Alignment -->\n @if (computedConfig.toolbar?.alignment) {\n <div class=\"ngx-editor-toolbar-group\">\n @if (computedConfig.toolbar?.alignment?.left !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.justifyLeft\"\n title=\"Align Left\"\n aria-label=\"Align Left\"\n (mousedown)=\"$event.preventDefault(); alignLeft()\"\n >\n <ngx-editor-icon name=\"alignLeft\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.alignment?.center !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.justifyCenter\"\n title=\"Align Center\"\n aria-label=\"Align Center\"\n (mousedown)=\"$event.preventDefault(); alignCenter()\"\n >\n <ngx-editor-icon name=\"alignCenter\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.alignment?.right !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.justifyRight\"\n title=\"Align Right\"\n aria-label=\"Align Right\"\n (mousedown)=\"$event.preventDefault(); alignRight()\"\n >\n <ngx-editor-icon name=\"alignRight\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.alignment?.justify !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.justifyFull\"\n title=\"Justify\"\n aria-label=\"Justify\"\n (mousedown)=\"$event.preventDefault(); alignJustify()\"\n >\n <ngx-editor-icon name=\"alignJustify\"></ngx-editor-icon>\n </button>\n }\n </div>\n }\n\n <!-- Lists & Indentation -->\n @if (computedConfig.toolbar?.list) {\n <div class=\"ngx-editor-toolbar-group\">\n @if (computedConfig.toolbar?.list?.unordered !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.insertUnorderedList\"\n title=\"Bullet List\"\n aria-label=\"Bullet List\"\n (mousedown)=\"$event.preventDefault(); insertUnorderedList()\"\n >\n <ngx-editor-icon name=\"unorderedList\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.list?.ordered !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.insertOrderedList\"\n title=\"Numbered List\"\n aria-label=\"Numbered List\"\n (mousedown)=\"$event.preventDefault(); insertOrderedList()\"\n >\n <ngx-editor-icon name=\"orderedList\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.list?.indent !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Increase Indent\"\n aria-label=\"Increase Indent\"\n (mousedown)=\"$event.preventDefault(); indent()\"\n >\n <ngx-editor-icon name=\"indent\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.list?.outdent !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Decrease Indent\"\n aria-label=\"Decrease Indent\"\n (mousedown)=\"$event.preventDefault(); outdent()\"\n >\n <ngx-editor-icon name=\"outdent\"></ngx-editor-icon>\n </button>\n }\n </div>\n }\n\n <!-- Insert Options -->\n @if (computedConfig.toolbar?.insert) {\n <div class=\"ngx-editor-toolbar-group\">\n @if (computedConfig.toolbar?.insert?.link !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"activeStates.link\"\n title=\"Insert Link (Ctrl+K)\"\n aria-label=\"Insert Link\"\n (click)=\"openLinkModal()\"\n >\n <ngx-editor-icon name=\"link\"></ngx-editor-icon>\n </button>\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Remove Link\"\n aria-label=\"Remove Link\"\n (mousedown)=\"$event.preventDefault(); unlink()\"\n >\n <ngx-editor-icon name=\"unlink\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.insert?.image !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Insert Image\"\n aria-label=\"Insert Image\"\n (click)=\"openImageModal()\"\n >\n <ngx-editor-icon name=\"image\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.insert?.horizontalRule !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Horizontal Line\"\n aria-label=\"Horizontal Line\"\n (mousedown)=\"$event.preventDefault(); insertHorizontalRule()\"\n >\n <ngx-editor-icon name=\"horizontalRule\"></ngx-editor-icon>\n </button>\n }\n </div>\n }\n\n <!-- Extra Actions -->\n @if (computedConfig.toolbar?.extra) {\n <div class=\"ngx-editor-toolbar-group\">\n @if (computedConfig.toolbar?.extra?.clearFormat !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n title=\"Clear Formatting\"\n aria-label=\"Clear Formatting\"\n (mousedown)=\"$event.preventDefault(); clearFormatting()\"\n >\n <ngx-editor-icon name=\"clearFormat\"></ngx-editor-icon>\n </button>\n }\n @if (computedConfig.toolbar?.extra?.sourceView !== false) {\n <button\n type=\"button\"\n class=\"ngx-editor-btn\"\n [class.is-active]=\"isSourceView\"\n title=\"View HTML Source\"\n aria-label=\"View HTML Source\"\n (click)=\"toggleSourceView()\"\n >\n <ngx-editor-icon name=\"sourceView\"></ngx-editor-icon>\n </button>\n }\n </div>\n }\n </div>\n }\n }\n\n <!-- Editor Content Area -->\n <div class=\"ngx-editor-content-wrapper\">\n <!-- Visual contenteditable -->\n <div\n #editorContent\n class=\"ngx-editor-content\"\n [style.display]=\"isSourceView ? 'none' : 'block'\"\n [style.height]=\"computedConfig.height\"\n [style.font-family]=\"fontFamily || computedConfig.fontFamily || null\"\n [style.font-size]=\"fontSize || computedConfig.fontSize || null\"\n [ngStyle]=\"parsedContentStyle\"\n [attr.contenteditable]=\"!disabled && !readonly\"\n [attr.spellcheck]=\"computedConfig.spellcheck\"\n [attr.data-placeholder]=\"computedConfig.placeholder\"\n [attr.data-empty]=\"isEmpty\"\n (input)=\"onEditorInput()\"\n (focus)=\"onEditorFocus($event)\"\n (blur)=\"onEditorBlur($event)\"\n (keydown)=\"onEditorKeydown($event)\"\n (keyup)=\"updateEditorState()\"\n (mouseup)=\"updateEditorState()\"\n (paste)=\"onEditorPaste()\"\n ></div>\n\n <!-- Raw HTML Source Code View -->\n @if (isSourceView) {\n <textarea\n #sourceTextarea\n class=\"ngx-editor-source\"\n [style.height]=\"computedConfig.height\"\n [disabled]=\"disabled || readonly\"\n [value]=\"content\"\n (input)=\"onSourceInput($event)\"\n ></textarea>\n }\n </div>\n\n <!-- Status Bar -->\n @if (computedConfig.showStatusBar) {\n <div class=\"ngx-editor-statusbar\">\n <div class=\"statusbar-left\">\n @if (activeBlockTag) {\n <span class=\"status-tag\">&lt;{{ activeBlockTag }}&gt;</span>\n }\n @if (isSourceView) {\n <span class=\"status-badge\">HTML SOURCE MODE</span>\n }\n </div>\n <div class=\"statusbar-right\">\n @if (computedConfig.showWordCount) {\n <span>Words: <strong>{{ stats.words }}</strong></span>\n }\n @if (computedConfig.maxCharacters) {\n <span [class.limit-reached]=\"stats.chars > computedConfig.maxCharacters\">\n {{ stats.chars }}/{{ computedConfig.maxCharacters }} Characters\n </span>\n } @else if (computedConfig.showCharCount) {\n <span>Characters: <strong>{{ stats.chars }}</strong></span>\n }\n </div>\n </div>\n }\n\n <!-- Link Modal -->\n @if (showLinkModal) {\n <div class=\"ngx-editor-modal-backdrop\" (click)=\"closeLinkModal()\">\n <div class=\"ngx-editor-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Insert Link</h3>\n <button type=\"button\" class=\"ngx-editor-btn\" (click)=\"closeLinkModal()\" aria-label=\"Close\">\n <ngx-editor-icon name=\"close\"></ngx-editor-icon>\n </button>\n </div>\n <div class=\"modal-body\">\n <div class=\"form-group\">\n <label>Link Text</label>\n <input\n type=\"text\"\n placeholder=\"Display text\"\n [(ngModel)]=\"linkModel.text\"\n />\n </div>\n <div class=\"form-group\">\n <label>Target URL</label>\n <input\n type=\"url\"\n placeholder=\"https://example.com\"\n [(ngModel)]=\"linkModel.url\"\n (keydown.enter)=\"insertLinkFromModal()\"\n />\n </div>\n <div class=\"form-group\">\n <label class=\"checkbox-label\">\n <input type=\"checkbox\" [(ngModel)]=\"linkModel.newTab\" />\n Open in a new tab\n </label>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn-secondary\" (click)=\"closeLinkModal()\">Cancel</button>\n <button\n type=\"button\"\n class=\"btn-primary\"\n [disabled]=\"!linkModel.url\"\n (click)=\"insertLinkFromModal()\"\n >\n Insert Link\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- Image Modal -->\n @if (showImageModal) {\n <div class=\"ngx-editor-modal-backdrop\" (click)=\"closeImageModal()\">\n <div class=\"ngx-editor-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"modal-header\">\n <h3>Insert Image</h3>\n <button type=\"button\" class=\"ngx-editor-btn\" (click)=\"closeImageModal()\" aria-label=\"Close\">\n <ngx-editor-icon name=\"close\"></ngx-editor-icon>\n </button>\n </div>\n <div class=\"modal-body\">\n <div class=\"form-group\">\n <label>Image URL</label>\n <input\n type=\"url\"\n placeholder=\"https://example.com/image.png\"\n [(ngModel)]=\"imageModel.url\"\n />\n </div>\n <div class=\"form-group\">\n <label>Or Upload File</label>\n <input\n type=\"file\"\n accept=\"image/*\"\n (change)=\"onImageFileUpload($event)\"\n />\n </div>\n <div class=\"form-group\">\n <label>Alt Description</label>\n <input\n type=\"text\"\n placeholder=\"Image description\"\n [(ngModel)]=\"imageModel.alt\"\n />\n </div>\n </div>\n <div class=\"modal-footer\">\n <button type=\"button\" class=\"btn-secondary\" (click)=\"closeImageModal()\">Cancel</button>\n <button\n type=\"button\"\n class=\"btn-primary\"\n [disabled]=\"!imageModel.url\"\n (click)=\"insertImageFromModal()\"\n >\n Insert Image\n </button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: [":host{display:block;font-family:var(--ngx-editor-font-family, inherit);box-sizing:border-box;--ngx-editor-font-family: inherit;--ngx-editor-font-size: 15px;--ngx-editor-line-height: 1.65;--ngx-editor-border: #e2e8f0;--ngx-editor-border-focus: #3b82f6;--ngx-editor-bg: #ffffff;--ngx-editor-toolbar-bg: #f8fafc;--ngx-editor-toolbar-btn-hover: #e2e8f0;--ngx-editor-toolbar-btn-active: #dbeafe;--ngx-editor-toolbar-btn-active-color: #1d4ed8;--ngx-editor-text: #1e293b;--ngx-editor-muted: #64748b;--ngx-editor-radius: 8px;--ngx-editor-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);--ngx-editor-shadow-focus: 0 0 0 3px rgba(59, 130, 246, .25);--ngx-editor-hashtag-color: #0069FF}.ngx-editor-container{display:flex;flex-direction:column;background-color:var(--ngx-editor-bg);border:1px solid var(--ngx-editor-border);border-radius:var(--ngx-editor-radius);box-shadow:var(--ngx-editor-shadow);overflow:hidden;transition:border-color .2s ease,box-shadow .2s ease;position:relative}.ngx-editor-container.is-focused{border-color:var(--ngx-editor-border-focus);box-shadow:var(--ngx-editor-shadow-focus)}.ngx-editor-container.is-disabled{background-color:#f1f5f9;opacity:.75;pointer-events:none;-webkit-user-select:none;user-select:none}.ngx-editor-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:6px 8px;background-color:var(--ngx-editor-toolbar-bg);border-bottom:1px solid var(--ngx-editor-border);-webkit-user-select:none;user-select:none;z-index:10}.ngx-editor-toolbar-group{display:flex;align-items:center;gap:2px}.ngx-editor-toolbar-group:not(:last-child):after{content:\"\";display:inline-block;width:1px;height:18px;background-color:var(--ngx-editor-border);margin:0 4px}.ngx-editor-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;padding:0;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--ngx-editor-text);cursor:pointer;transition:background-color .15s ease,color .15s ease,border-color .15s ease;outline:none}.ngx-editor-btn:hover:not(:disabled){background-color:var(--ngx-editor-toolbar-btn-hover)}.ngx-editor-btn:active:not(:disabled),.ngx-editor-btn.is-active{background-color:var(--ngx-editor-toolbar-btn-active);color:var(--ngx-editor-toolbar-btn-active-color);border-color:#bfdbfe}.ngx-editor-btn:disabled{opacity:.35;cursor:not-allowed}.ngx-editor-btn svg{display:block;pointer-events:none}.ngx-editor-select{height:32px;padding:0 8px;font-size:13px;color:var(--ngx-editor-text);background-color:var(--ngx-editor-bg);border:1px solid var(--ngx-editor-border);border-radius:4px;outline:none;cursor:pointer;transition:border-color .15s ease}.ngx-editor-select:hover{border-color:#cbd5e1}.ngx-editor-select:focus{border-color:var(--ngx-editor-border-focus)}.ngx-editor-color-btn{position:relative}.ngx-editor-color-btn .color-bar{position:absolute;bottom:4px;left:6px;right:6px;height:3px;border-radius:1px}.ngx-color-popover{position:absolute;top:100%;left:0;margin-top:4px;background:#fff;border:1px solid var(--ngx-editor-border);border-radius:6px;box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;padding:8px;z-index:50;width:176px}.ngx-color-popover .color-grid{display:grid;grid-template-columns:repeat(6,1fr);gap:4px;margin-bottom:8px}.ngx-color-popover .color-swatch{width:22px;height:22px;border-radius:3px;border:1px solid rgba(0,0,0,.15);cursor:pointer;padding:0;transition:transform .1s ease}.ngx-color-popover .color-swatch:hover{transform:scale(1.15);z-index:2}.ngx-color-popover .custom-color-picker{display:flex;align-items:center;gap:8px;font-size:12px;color:var(--ngx-editor-muted)}.ngx-color-popover .custom-color-picker input[type=color]{width:28px;height:24px;border:1px solid var(--ngx-editor-border);border-radius:3px;padding:0;cursor:pointer;background:none}.ngx-editor-content-wrapper{position:relative;flex:1;display:flex;flex-direction:column}.ngx-editor-content{flex:1;padding:16px;outline:none;font-family:var(--ngx-editor-font-family, inherit);font-size:var(--ngx-editor-font-size, 15px);line-height:var(--ngx-editor-line-height, 1.65);color:var(--ngx-editor-text);overflow-y:auto;min-height:200px;word-break:break-word}.ngx-editor-content p{margin:0 0 1em}.ngx-editor-content h1{font-size:2em;font-weight:700;margin:.67em 0;line-height:1.25}.ngx-editor-content h2{font-size:1.6em;font-weight:600;margin:.75em 0;line-height:1.3}.ngx-editor-content h3{font-size:1.3em;font-weight:600;margin:.83em 0}.ngx-editor-content h4{font-size:1.1em;font-weight:600;margin:1em 0}.ngx-editor-content ul,.ngx-editor-content ol{margin:0 0 1em 1.5em;padding:0}.ngx-editor-content li{margin-bottom:.25em}.ngx-editor-content blockquote{margin:1em 0;padding:.5em 1em;border-left:4px solid var(--ngx-editor-border-focus);background-color:#f8fafc;color:var(--ngx-editor-muted);font-style:italic}.ngx-editor-content pre{background-color:#1e293b;color:#f8fafc;padding:12px;border-radius:6px;font-family:Consolas,Monaco,Courier New,monospace;font-size:13px;overflow-x:auto;margin:1em 0}.ngx-editor-content code{background-color:#f1f5f9;color:#e11d48;padding:2px 5px;border-radius:4px;font-family:Consolas,Monaco,Courier New,monospace;font-size:.9em}.ngx-editor-content a{color:#2563eb;text-decoration:underline;text-underline-offset:2px;cursor:pointer}.ngx-editor-content .ngx-editor-hashtag,.ngx-editor-content .post-content-hashtag{color:var(--ngx-editor-hashtag-color, #0069FF)!important;font-weight:500;display:inline;cursor:text}.ngx-editor-content hr{border:none;border-top:1px solid var(--ngx-editor-border);margin:1.5em 0}.ngx-editor-content img{max-width:100%;height:auto;border-radius:4px}.ngx-editor-content[data-empty=true]:before{content:attr(data-placeholder);position:absolute;color:#94a3b8;pointer-events:none;-webkit-user-select:none;user-select:none}.ngx-editor-content:has(ol,ul,li,table,img,hr,blockquote,pre):before{display:none!important;content:none!important}.ngx-editor-source{width:100%;flex:1;min-height:200px;padding:16px;border:none;outline:none;font-family:Consolas,Monaco,Courier New,monospace;font-size:13.5px;line-height:1.5;background-color:#0f172a;color:#38bdf8;resize:vertical;box-sizing:border-box}.ngx-editor-statusbar{display:flex;align-items:center;justify-content:space-between;padding:6px 12px;background-color:var(--ngx-editor-toolbar-bg);border-top:1px solid var(--ngx-editor-border);font-size:12px;color:var(--ngx-editor-muted)}.ngx-editor-statusbar .statusbar-left,.ngx-editor-statusbar .statusbar-right{display:flex;align-items:center;gap:12px}.ngx-editor-statusbar .status-tag{background-color:#e2e8f0;padding:2px 6px;border-radius:4px;font-weight:600;text-transform:uppercase;font-size:10px}.ngx-editor-statusbar .status-badge{background-color:#fee2e2;color:#ef4444;padding:2px 6px;border-radius:4px;font-weight:500;font-size:11px}.ngx-editor-modal-backdrop{position:absolute;inset:0;background:#0f172a66;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:100;animation:fadeIn .15s ease-out}.ngx-editor-modal{background:#fff;border-radius:8px;box-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;width:90%;max-width:400px;padding:20px;box-sizing:border-box}.ngx-editor-modal .modal-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}.ngx-editor-modal .modal-header h3{margin:0;font-size:16px;font-weight:600;color:var(--ngx-editor-text)}.ngx-editor-modal .modal-body{display:flex;flex-direction:column;gap:12px}.ngx-editor-modal .form-group{display:flex;flex-direction:column;gap:4px}.ngx-editor-modal .form-group label{font-size:13px;font-weight:500;color:var(--ngx-editor-text)}.ngx-editor-modal .form-group input[type=text],.ngx-editor-modal .form-group input[type=url]{padding:8px 12px;border:1px solid var(--ngx-editor-border);border-radius:6px;font-size:14px;outline:none;transition:border-color .15s ease}.ngx-editor-modal .form-group input[type=text]:focus,.ngx-editor-modal .form-group input[type=url]:focus{border-color:var(--ngx-editor-border-focus)}.ngx-editor-modal .form-group .checkbox-label{display:flex;align-items:center;gap:8px;cursor:pointer;font-size:13px;color:var(--ngx-editor-text);-webkit-user-select:none;user-select:none}.ngx-editor-modal .modal-footer{display:flex;justify-content:flex-end;gap:8px;margin-top:20px}.ngx-editor-modal .modal-footer button{padding:7px 14px;border-radius:6px;font-size:13px;font-weight:500;cursor:pointer;transition:all .15s ease;outline:none}.ngx-editor-modal .modal-footer .btn-secondary{background:#f1f5f9;border:1px solid var(--ngx-editor-border);color:var(--ngx-editor-text)}.ngx-editor-modal .modal-footer .btn-secondary:hover{background:#e2e8f0}.ngx-editor-modal .modal-footer .btn-primary{background:#2563eb;border:1px solid #2563eb;color:#fff}.ngx-editor-modal .modal-footer .btn-primary:hover{background:#1d4ed8}.ngx-editor-modal .modal-footer .btn-primary:disabled{opacity:.5;cursor:not-allowed}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}\n"] }]
1905
+ }], ctorParameters: () => [{ type: EditorCommandService }, { type: i2.DomSanitizer }, { type: i0.ElementRef }, { type: i0.ChangeDetectorRef }], propDecorators: { editorContentRef: [{
1906
+ type: ViewChild,
1907
+ args: ['editorContent', { static: false }]
1908
+ }], sourceTextareaRef: [{
1909
+ type: ViewChild,
1910
+ args: ['sourceTextarea', { static: false }]
1911
+ }], value: [{
1912
+ type: Input
1913
+ }], placeholder: [{
1914
+ type: Input
1915
+ }], disabled: [{
1916
+ type: Input
1917
+ }], readonly: [{
1918
+ type: Input
1919
+ }], showToolbar: [{
1920
+ type: Input
1921
+ }], showStatusBar: [{
1922
+ type: Input
1923
+ }], height: [{
1924
+ type: Input
1925
+ }], minHeight: [{
1926
+ type: Input
1927
+ }], maxHeight: [{
1928
+ type: Input
1929
+ }], tools: [{
1930
+ type: Input
1931
+ }], maxCharacters: [{
1932
+ type: Input
1933
+ }], fontFamily: [{
1934
+ type: Input
1935
+ }], fontSize: [{
1936
+ type: Input
1937
+ }], fontFamilies: [{
1938
+ type: Input
1939
+ }], customClass: [{
1940
+ type: Input
1941
+ }], editorStyle: [{
1942
+ type: Input
1943
+ }], contentStyle: [{
1944
+ type: Input
1945
+ }], hashtagColor: [{
1946
+ type: Input
1947
+ }], highlightHashtags: [{
1948
+ type: Input
1949
+ }], config: [{
1950
+ type: Input
1951
+ }], valueChange: [{
1952
+ type: Output
1953
+ }], textChange: [{
1954
+ type: Output
1955
+ }], editorFocus: [{
1956
+ type: Output
1957
+ }], editorBlur: [{
1958
+ type: Output
1959
+ }], onDocumentClick: [{
1960
+ type: HostListener,
1961
+ args: ['document:click', ['$event']]
1962
+ }] } });
1963
+
1964
+ /*
1965
+ * Public API Surface of ngx-rich-text-editor
1966
+ */
1967
+
1968
+ /**
1969
+ * Generated bundle index. Do not edit.
1970
+ */
1971
+
1972
+ export { DEFAULT_COLOR_PALETTE, DEFAULT_EDITOR_CONFIG, DEFAULT_FONT_FAMILIES, DEFAULT_TOOLBAR_CONFIG, EDITOR_ICONS, EditorCommandService, NgxEditorIconComponent, NgxEditorToolbarComponent, NgxRichTextEditorComponent };
1973
+ //# sourceMappingURL=ngx-rich-text-editor.mjs.map