@esfaenza/text-editors 19.2.9 → 19.2.10

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.
@@ -1,666 +1,522 @@
1
- import * as i0 from '@angular/core';
2
- import { InjectionToken, EventEmitter, inject, forwardRef, Output, Input, ViewEncapsulation, Component, input, viewChild } from '@angular/core';
3
- import * as i1 from '@angular/common';
4
1
  import { CommonModule } from '@angular/common';
2
+ import * as i0 from '@angular/core';
3
+ import { viewChild, input, booleanAttribute, EventEmitter, forwardRef, Output, ViewEncapsulation, Component, InjectionToken } from '@angular/core';
4
+ import * as i1 from '@angular/forms';
5
5
  import { FormsModule, NG_VALUE_ACCESSOR, NG_VALIDATORS } from '@angular/forms';
6
- import * as i2 from '@ckeditor/ckeditor5-angular';
7
- import { CKEditorModule, loadCKEditorCloud } from '@ckeditor/ckeditor5-angular';
6
+ import { Extension, Editor } from '@tiptap/core';
7
+ import { Markdown } from '@tiptap/markdown';
8
+ import { Mention } from '@tiptap/extension-mention';
9
+ import { StarterKit } from '@tiptap/starter-kit';
10
+ import { TextStyle } from '@tiptap/extension-text-style';
11
+ import { Color } from '@tiptap/extension-color';
12
+ import { Underline } from '@tiptap/extension-underline';
13
+ import { Table } from '@tiptap/extension-table';
14
+ import { TableRow } from '@tiptap/extension-table-row';
15
+ import { TableCell } from '@tiptap/extension-table-cell';
16
+ import { TableHeader } from '@tiptap/extension-table-header';
17
+ import { TextAlign } from '@tiptap/extension-text-align';
18
+ import { Image } from '@tiptap/extension-image';
19
+ import { Plugin } from '@tiptap/pm/state';
8
20
 
9
- const TXE_DEBUG = new InjectionToken('TXE_DEBUG');
21
+ const FontSize = Extension.create({
22
+ name: 'fontSize',
23
+ addGlobalAttributes() {
24
+ return [
25
+ {
26
+ types: ['textStyle'],
27
+ attributes: {
28
+ fontSize: {
29
+ default: null,
30
+ parseHTML: element => {
31
+ const size = element.style.fontSize;
32
+ if (!size)
33
+ return null;
34
+ // normalize pt → px
35
+ if (size.endsWith('pt'))
36
+ return `${Math.round(parseFloat(size) * 1.333)}px`;
37
+ return size;
38
+ },
39
+ renderHTML: attributes => {
40
+ if (!attributes.fontSize)
41
+ return {};
42
+ return { style: `font-size: ${attributes.fontSize}` };
43
+ },
44
+ },
45
+ },
46
+ },
47
+ ];
48
+ },
49
+ addCommands() {
50
+ return {
51
+ setFontSize: (fontSize) => ({ chain }) => { return chain().setMark('textStyle', { fontSize }).run(); },
52
+ unsetFontSize: () => ({ chain }) => { return chain().setMark('textStyle', { fontSize: null }).removeEmptyTextStyle().run(); },
53
+ };
54
+ },
55
+ });
10
56
 
11
- class HtmlEditorComponent {
12
- constructor() {
13
- this.Readonly = false; // Angular19 -> [disabled]
14
- this.Required = false;
15
- this.Validation = false;
16
- this.onEditorLoaded = new EventEmitter();
17
- this.EditorModel = null;
18
- this.DEBUG = inject(TXE_DEBUG, { optional: true }) || false;
19
- //#region Forms
20
- this.onTouched = () => { };
21
- this.propagateChange = (_) => { };
22
- }
23
- initializeEditor() {
24
- if (!this.PersistentEditor) {
25
- if (this.DEBUG)
26
- console.error("@ HTML-Editor @: Impossibile inizializzare l'editor HTML senza PersistentEditor");
27
- }
28
- else
29
- this.PersistentEditor?.iInitializeEditor("@", [], (source) => {
30
- this.onEditorLoaded.emit(source);
31
- });
32
- }
33
- ngOnInit() {
34
- if (this.DEBUG)
35
- console.info("@ HTML-Editor @: Il componente HtmlEditorComponent è stato creato");
36
- }
37
- ngOnDestroy() {
38
- if (this.DEBUG)
39
- console.info("@ HTML-Editor @: Il componente HtmlEditorComponent è stato distrutto");
40
- }
41
- registerOnChange(fn) {
42
- this.propagateChange = fn;
43
- }
44
- registerOnTouched(fn) {
45
- this.onTouched = fn;
46
- }
47
- writeValue(obj) {
48
- let value = "";
49
- if (obj !== undefined)
50
- value = obj;
51
- if (this.DEBUG)
52
- console.log("@ HTML-Editor @: Writevalue pre-refresh: " + value);
53
- if (value === this.EditorModel?.value) {
54
- if (this.DEBUG)
55
- console.log("@ HTML-Editor @: Writevalue ridondante");
56
- return;
57
- }
58
- this.EditorModel = null;
59
- requestAnimationFrame(() => {
60
- if (this.DEBUG)
61
- console.log("@ HTML-Editor @: Writevalue post-refresh: " + value);
62
- this.EditorModel = { value: value, onEditorChange: this.onEditorChange.bind(this) };
63
- });
64
- }
65
- validate(c) {
66
- let ret = null;
67
- if (this.EditorModel?.value || this.Readonly || !this.Required)
68
- ret = null;
69
- ret = null;
70
- if (this.DEBUG)
71
- console.log("@ HTML-Editor @: Validate post-validate: " + ret);
72
- return ret;
73
- }
74
- onEditorChange({ editor }) {
75
- if (!this.EditorModel)
76
- return;
77
- let data = editor?.getData();
78
- if (this.DEBUG)
79
- console.log("@ HTML-Editor @: changed: " + data);
80
- this.propagateChange(data);
81
- }
82
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: HtmlEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
83
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.15", type: HtmlEditorComponent, isStandalone: true, selector: "html-editor", inputs: { PersistentEditor: "PersistentEditor", Readonly: "Readonly", Required: "Required", Validation: "Validation" }, outputs: { onEditorLoaded: "onEditorLoaded" }, providers: [
84
- { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => HtmlEditorComponent), multi: true },
85
- { provide: NG_VALIDATORS, useExisting: forwardRef(() => HtmlEditorComponent), multi: true }
86
- ], ngImport: i0, template: "<div class=\"html-editor\" *ngIf=\"EditorModel && PersistentEditor\">\r\n <ng-content *ngIf=\"PersistentEditor.EditorTemplate()\">\r\n <ng-content *ngTemplateOutlet=\"PersistentEditor.EditorTemplate(); context: {$implicit: EditorModel}\"></ng-content>\r\n </ng-content>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap\";:root{--ck-content-font-family: \"Lato\"}@media print{body{margin:0!important}}.main-container{font-family:var(--ck-content-font-family);width:fit-content;margin-left:auto;margin-right:auto}.editor-container_classic-editor .editor-container__editor{min-width:795px;max-width:795px}.html-editor .ck-content{overflow-x:hidden!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CKEditorModule }], encapsulation: i0.ViewEncapsulation.None }); }
87
- }
88
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: HtmlEditorComponent, decorators: [{
89
- type: Component,
90
- args: [{ selector: "html-editor", standalone: true, imports: [CommonModule, FormsModule, CKEditorModule], encapsulation: ViewEncapsulation.None, providers: [
91
- { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => HtmlEditorComponent), multi: true },
92
- { provide: NG_VALIDATORS, useExisting: forwardRef(() => HtmlEditorComponent), multi: true }
93
- ], template: "<div class=\"html-editor\" *ngIf=\"EditorModel && PersistentEditor\">\r\n <ng-content *ngIf=\"PersistentEditor.EditorTemplate()\">\r\n <ng-content *ngTemplateOutlet=\"PersistentEditor.EditorTemplate(); context: {$implicit: EditorModel}\"></ng-content>\r\n </ng-content>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap\";:root{--ck-content-font-family: \"Lato\"}@media print{body{margin:0!important}}.main-container{font-family:var(--ck-content-font-family);width:fit-content;margin-left:auto;margin-right:auto}.editor-container_classic-editor .editor-container__editor{min-width:795px;max-width:795px}.html-editor .ck-content{overflow-x:hidden!important}\n"] }]
94
- }], propDecorators: { PersistentEditor: [{
95
- type: Input
96
- }], Readonly: [{
97
- type: Input
98
- }], Required: [{
99
- type: Input
100
- }], Validation: [{
101
- type: Input
102
- }], onEditorLoaded: [{
103
- type: Output
104
- }] } });
57
+ const FontFamily = Extension.create({
58
+ name: 'fontFamily',
59
+ addGlobalAttributes() {
60
+ return [
61
+ {
62
+ types: ['textStyle'],
63
+ attributes: {
64
+ fontFamily: {
65
+ default: null,
66
+ parseHTML: element => element.style.fontFamily,
67
+ renderHTML: attributes => {
68
+ if (!attributes.fontFamily)
69
+ return {};
70
+ return { style: `font-family: ${attributes.fontFamily}` };
71
+ },
72
+ },
73
+ },
74
+ },
75
+ ];
76
+ },
77
+ addCommands() {
78
+ return {
79
+ setFontFamily: (fontFamily) => ({ chain }) => { return chain().setMark('textStyle', { fontFamily }).run(); },
80
+ unsetFontFamily: () => ({ chain }) => { return chain().setMark('textStyle', { fontFamily: null, }).removeEmptyTextStyle().run(); },
81
+ };
82
+ },
83
+ });
105
84
 
106
- class MarkdownEditorComponent {
107
- constructor() {
108
- this.Readonly = false; // Angular19 -> [disabled]
109
- this.Required = false;
110
- this.Validation = false;
111
- // Mention
112
- this.MentionMarker = "@";
113
- this.MentionEntities = [];
114
- this.onEditorLoaded = new EventEmitter();
115
- this.DEBUG = inject(TXE_DEBUG, { optional: true }) || false;
116
- this.EditorModel = null;
117
- //#region Forms
118
- this.onTouched = () => { };
119
- this.propagateChange = (_) => { };
120
- }
121
- get NormalizedMentionEntities() {
122
- return this.MentionEntities.map(m => {
123
- return { id: this.MentionMarker + m.id, description: m.description };
124
- });
125
- }
126
- initializeEditor() {
127
- if (!this.PersistentEditor) {
128
- if (this.DEBUG)
129
- console.error("@ Markdown-Editor @: Impossibile inizializzare l'editor HTML senza PersistentEditor");
130
- }
131
- else
132
- this.PersistentEditor?.iInitializeEditor(this.MentionMarker, this.NormalizedMentionEntities, (source) => {
133
- this.onEditorLoaded.emit(source);
134
- });
135
- }
136
- ngOnInit() {
137
- if (this.DEBUG)
138
- console.info("@ Markdown-Editor @: Il componente MarkdownEditor è stato creato");
139
- }
140
- ngOnDestroy() {
141
- if (this.DEBUG)
142
- console.info("@ Markdown-Editor @: Il componente MarkdownEditor è stato distrutto");
143
- }
144
- registerOnChange(fn) {
145
- this.propagateChange = fn;
146
- }
147
- registerOnTouched(fn) {
148
- this.onTouched = fn;
149
- }
150
- writeValue(obj) {
151
- let value = "";
152
- if (obj !== undefined)
153
- value = obj;
154
- if (this.DEBUG)
155
- console.log("@ Markdown-Editor @: Writevalue pre-refresh: " + value);
156
- if (value === this.EditorModel?.value) {
157
- if (this.DEBUG)
158
- console.log("@ Markdown-Editor @: Writevalue ridondante");
159
- return;
85
+ const LineHeight = Extension.create({
86
+ name: 'lineHeight',
87
+ addGlobalAttributes() {
88
+ return [
89
+ {
90
+ types: ['paragraph', 'heading'],
91
+ attributes: {
92
+ lineHeight: {
93
+ default: null,
94
+ parseHTML: element => element.style.lineHeight,
95
+ renderHTML: attributes => {
96
+ if (!attributes.lineHeight)
97
+ return {};
98
+ return { style: `line-height: ${attributes.lineHeight}` };
99
+ },
100
+ },
101
+ },
102
+ },
103
+ ];
104
+ },
105
+ addCommands() {
106
+ return {
107
+ setLineHeight: (lineHeight) => ({ commands }) => { return commands.updateAttributes('paragraph', { lineHeight }); },
108
+ unsetLineHeight: () => ({ commands }) => { return commands.updateAttributes('paragraph', { lineHeight: null }); },
109
+ };
110
+ },
111
+ });
112
+
113
+ let Suggestion = (choiches) => {
114
+ return {
115
+ char: '@',
116
+ items: ({ query }) => {
117
+ return choiches.filter(item => item.description.toLowerCase().startsWith(query.toLowerCase())).slice(0, 5);
118
+ },
119
+ render: () => {
120
+ let popup;
121
+ let selectedIndex = 0;
122
+ let currentProps;
123
+ return {
124
+ onStart: (props) => {
125
+ popup = document.createElement('div');
126
+ popup.classList.add('suggestion-popup');
127
+ document.body.appendChild(popup);
128
+ currentProps = props;
129
+ updatePopup();
130
+ },
131
+ onKeyDown(props) {
132
+ if (props.event.key === 'ArrowUp') {
133
+ selectedIndex = (selectedIndex + currentProps.items.length - 1) % currentProps.items.length;
134
+ updatePopup();
135
+ return true;
136
+ }
137
+ if (props.event.key === 'ArrowDown') {
138
+ selectedIndex = (selectedIndex + 1) % currentProps.items.length;
139
+ updatePopup();
140
+ return true;
141
+ }
142
+ if (props.event.key === 'Enter') {
143
+ selectItem();
144
+ return true;
145
+ }
146
+ return false;
147
+ },
148
+ onUpdate(props) {
149
+ currentProps = props;
150
+ updatePopup();
151
+ },
152
+ onExit() { popup.remove(); },
153
+ };
154
+ function updatePopup() {
155
+ if (!currentProps.clientRect)
156
+ return;
157
+ popup.innerHTML = '';
158
+ currentProps.items.forEach((item, index) => {
159
+ const button = document.createElement('button');
160
+ button.textContent = item.description;
161
+ if (index === selectedIndex)
162
+ button.classList.add('is-selected');
163
+ button.addEventListener('click', () => {
164
+ selectedIndex = index;
165
+ selectItem();
166
+ });
167
+ popup.appendChild(button);
168
+ });
169
+ const rect = currentProps.clientRect();
170
+ popup.style.position = 'fixed';
171
+ popup.style.left = `${rect.left}px`;
172
+ popup.style.top = `${rect.bottom + 5}px`;
173
+ }
174
+ function selectItem() {
175
+ const item = currentProps.items[selectedIndex];
176
+ if (item)
177
+ currentProps.command({ id: item.id, description: item.description, });
178
+ }
160
179
  }
161
- this.EditorModel = null;
162
- requestAnimationFrame(() => {
163
- if (this.DEBUG)
164
- console.log("@ Markdown-Editor @: Writevalue post-refresh: " + value);
165
- this.EditorModel = { value: value, onEditorChange: this.onEditorChange.bind(this) };
166
- });
167
- }
168
- validate(c) {
169
- let ret = null;
170
- if (this.EditorModel?.value || this.Readonly || !this.Required)
171
- ret = null;
172
- ret = null;
173
- if (this.DEBUG)
174
- console.log("@ Markdown-Editor @: Validate post-validate: " + ret);
175
- return ret;
176
- }
177
- onEditorChange({ editor }) {
178
- if (!this.EditorModel)
179
- return;
180
- let data = editor?.getData();
181
- if (this.DEBUG)
182
- console.log("@ Markdown-Editor @: changed: " + data);
183
- this.propagateChange(data);
184
- }
185
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MarkdownEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
186
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.15", type: MarkdownEditorComponent, isStandalone: true, selector: "markdown-editor", inputs: { PersistentEditor: "PersistentEditor", Readonly: "Readonly", Required: "Required", Validation: "Validation", MentionMarker: "MentionMarker", MentionEntities: "MentionEntities" }, outputs: { onEditorLoaded: "onEditorLoaded" }, providers: [
187
- { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MarkdownEditorComponent), multi: true },
188
- { provide: NG_VALIDATORS, useExisting: forwardRef(() => MarkdownEditorComponent), multi: true }
189
- ], ngImport: i0, template: "<div class=\"html-editor\" *ngIf=\"EditorModel && PersistentEditor\">\r\n <ng-content *ngIf=\"PersistentEditor.EditorTemplate()\">\r\n <ng-content *ngTemplateOutlet=\"PersistentEditor.EditorTemplate(); context: {$implicit: EditorModel}\"></ng-content>\r\n </ng-content>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap\";:root{--ck-content-font-family: \"Lato\"}@media print{body{margin:0!important}}.main-container{font-family:var(--ck-content-font-family);width:fit-content;margin-left:auto;margin-right:auto}.editor-container_classic-editor .editor-container__editor{min-width:795px;max-width:795px}.markdown-editor .ck-content{overflow-x:hidden!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CKEditorModule }], encapsulation: i0.ViewEncapsulation.None }); }
190
- }
191
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MarkdownEditorComponent, decorators: [{
192
- type: Component,
193
- args: [{ selector: "markdown-editor", standalone: true, imports: [CommonModule, FormsModule, CKEditorModule], encapsulation: ViewEncapsulation.None, providers: [
194
- { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => MarkdownEditorComponent), multi: true },
195
- { provide: NG_VALIDATORS, useExisting: forwardRef(() => MarkdownEditorComponent), multi: true }
196
- ], template: "<div class=\"html-editor\" *ngIf=\"EditorModel && PersistentEditor\">\r\n <ng-content *ngIf=\"PersistentEditor.EditorTemplate()\">\r\n <ng-content *ngTemplateOutlet=\"PersistentEditor.EditorTemplate(); context: {$implicit: EditorModel}\"></ng-content>\r\n </ng-content>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap\";:root{--ck-content-font-family: \"Lato\"}@media print{body{margin:0!important}}.main-container{font-family:var(--ck-content-font-family);width:fit-content;margin-left:auto;margin-right:auto}.editor-container_classic-editor .editor-container__editor{min-width:795px;max-width:795px}.markdown-editor .ck-content{overflow-x:hidden!important}\n"] }]
197
- }], propDecorators: { PersistentEditor: [{
198
- type: Input
199
- }], Readonly: [{
200
- type: Input
201
- }], Required: [{
202
- type: Input
203
- }], Validation: [{
204
- type: Input
205
- }], MentionMarker: [{
206
- type: Input
207
- }], MentionEntities: [{
208
- type: Input
209
- }], onEditorLoaded: [{
210
- type: Output
211
- }] } });
180
+ };
181
+ };
212
182
 
213
- const cloudConfig$2 = {
214
- version: '46.1.1',
215
- translations: ['it']
183
+ let handleFiles = async (view, files) => {
184
+ const file = Array.from(files || []).find(f => f.type.startsWith('image/'));
185
+ if (!file)
186
+ return;
187
+ const reader = new FileReader();
188
+ reader.onload = () => {
189
+ const base64 = reader.result;
190
+ const img = new window.Image();
191
+ img.onload = () => {
192
+ const { schema } = view.state;
193
+ const node = schema.nodes.image.create({ src: base64, width: img.naturalWidth, float: "none" });
194
+ const tr = view.state.tr.replaceSelectionWith(node);
195
+ view.dispatch(tr);
196
+ };
197
+ img.src = base64;
198
+ };
199
+ reader.readAsDataURL(file);
216
200
  };
217
- const LICENSE_KEY$2 = 'eyJhbGciOiJFUzI1NiJ9.eyJleHAiOjE4MDQ5ODIzOTksImp0aSI6ImJkZDU4ZGM3LTlhYzgtNDA4Mi05ZDk3LTg3Yzc0NjEzMDlmNyIsInVzYWdlRW5kcG9pbnQiOiJodHRwczovL3Byb3h5LWV2ZW50LmNrZWRpdG9yLmNvbSIsImRpc3RyaWJ1dGlvbkNoYW5uZWwiOlsiY2xvdWQiLCJkcnVwYWwiXSwid2hpdGVMYWJlbCI6dHJ1ZSwiZmVhdHVyZXMiOlsiRFJVUCIsIkRPIiwiRlAiLCJTQyIsIlRPQyIsIlRQTCIsIlBPRSIsIkNDIiwiTUYiLCJFMlAiLCJFMlciLCJNTEwiLCJTRUUiLCJFQ0giLCJFSVMiLCJMSCIsIkZPTyJdLCJ2YyI6IjdjZjg2OGYwIn0.vw-04yWGiTzZVSaGxQUGCxWXBUQXRJ0yB2ItqwvNnd7nEjMFC4d9kiDgbkzGnHgAjkiQEUqTzrM7b1z4cR6rhA';
218
- class HtmlEditorStandaloneComponent {
219
- constructor() {
220
- this.Readonly = false; // Angular19 -> [disabled]
221
- this.Required = false;
222
- this.Validation = false;
223
- this.onEditorLoaded = new EventEmitter();
224
- //#region CKEditor5
225
- this.Editor = null;
226
- this.EditorConfig = null;
227
- this.EditorModel = { editorDataText: '', };
228
- //#endregion
229
- //#region Forms
230
- this.onTouched = () => { };
231
- this.propagateChange = (_) => { };
232
- }
233
- initializeEditor() {
234
- if (!this.Editor) {
235
- loadCKEditorCloud(cloudConfig$2).then(this.internalInitializeEditor.bind(this));
236
- this.onEditorLoaded.emit();
237
- }
238
- }
239
- ngOnDestroy() {
240
- console.warn("Il componente HtmlEditorComponent è stato distrutto");
241
- }
242
- internalInitializeEditor(cloudConfig) {
243
- const { ClassicEditor,
244
- /* Plugins */
245
- Alignment, AutoImage, Autosave, Base64UploadAdapter, Bold, CloudServices, Essentials, GeneralHtmlSupport, Heading, ImageInline, ImageBlock, ImageInsert, ImageInsertViaUrl, ImageResize, ImageStyle, ImageToolbar, ImageUpload, Italic, Link, List, Paragraph, PasteFromOffice, Underline, SourceEditing } = cloudConfig.CKEditor;
246
- this.Editor = ClassicEditor;
247
- this.EditorConfig = {
248
- toolbar: {
249
- items: ['heading', '|', 'bold', 'italic', 'underline', '|', 'undo', 'redo', '|', 'insertImage', 'imageStyle:block', 'imageStyle:alignLeft', 'imageStyle:alignRight', '|', 'sourceEditing'],
250
- shouldNotGroupWhenFull: false
251
- },
252
- plugins: [
253
- Alignment, AutoImage, Autosave, Base64UploadAdapter, Bold, CloudServices, Essentials, GeneralHtmlSupport,
254
- Heading, ImageInline, ImageBlock, ImageInsert, ImageInsertViaUrl, ImageResize, ImageStyle, ImageToolbar, ImageUpload, Italic,
255
- Link, List, Paragraph, PasteFromOffice, Underline, SourceEditing
256
- ],
257
- heading: {
258
- options: [
259
- { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
260
- { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
261
- { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
262
- { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
263
- { model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' },
264
- { model: 'heading5', view: 'h5', title: 'Heading 5', class: 'ck-heading_heading5' },
265
- { model: 'heading6', view: 'h6', title: 'Heading 6', class: 'ck-heading_heading6' }
266
- ]
267
- },
268
- htmlSupport: {
269
- allow: [{ name: /^.*$/, styles: true, attributes: true, classes: true }]
201
+ const CustomImage = Image.extend({
202
+ draggable: true,
203
+ selectable: true,
204
+ addAttributes() {
205
+ return {
206
+ ...this.parent?.(),
207
+ width: { default: null, },
208
+ float: { default: "none" }, // 'left' | 'right' | 'none'
209
+ src: {
210
+ default: null,
211
+ parseHTML: element => { return element.getAttribute('src'); },
212
+ renderHTML: attributes => { return { src: attributes.src, }; },
270
213
  },
271
- image: {
272
- toolbar: ['imageStyle:block', 'imageStyle:side', '|', 'resizeImage:original'],
273
- insert: { type: 'auto' }
214
+ };
215
+ },
216
+ renderHTML({ HTMLAttributes }) {
217
+ const { float } = HTMLAttributes;
218
+ let style = '';
219
+ if (float === 'left')
220
+ style = 'float:left; display:block; margin-right: 10px';
221
+ else if (float === 'right')
222
+ style = 'float:right; display:block; margin-left: 10px';
223
+ else
224
+ style = `display: inline-block; float: none; vertical-align: middle;`;
225
+ return ['img', { ...HTMLAttributes, style }];
226
+ },
227
+ parseHTML() {
228
+ return [
229
+ {
230
+ tag: 'img[src]',
231
+ getAttrs: element => {
232
+ const el = element;
233
+ const src = el.getAttribute('src');
234
+ if (!src)
235
+ return false;
236
+ // allow both normal URLs AND base64
237
+ if (src.startsWith('http') || src.startsWith('data:image'))
238
+ return { src, float: el.style.float || null, width: el.style.width ? parseInt(el.style.width) : null, };
239
+ return false;
240
+ },
274
241
  },
275
- initialData: "",
276
- language: 'it',
277
- licenseKey: LICENSE_KEY$2,
278
- link: {
279
- addTargetToExternalLinks: true,
280
- defaultProtocol: 'https://',
281
- decorators: {
282
- toggleDownloadable: {
283
- mode: 'manual',
284
- label: 'Downloadable',
285
- attributes: { download: 'file' }
286
- }
242
+ ];
243
+ },
244
+ addProseMirrorPlugins: () => {
245
+ return [
246
+ new Plugin({
247
+ props: {
248
+ handleDrop: (view, event) => {
249
+ const files = event.dataTransfer?.files;
250
+ if (!files?.length)
251
+ return false;
252
+ handleFiles(view, files);
253
+ return true;
254
+ },
255
+ handlePaste: (view, event) => {
256
+ const files = event.clipboardData?.files;
257
+ if (!files?.length)
258
+ return false;
259
+ handleFiles(view, files);
260
+ return true;
261
+ },
262
+ },
263
+ })
264
+ ];
265
+ },
266
+ });
267
+ const CustomImageWithView = CustomImage.extend({
268
+ addNodeView: () => {
269
+ return ({ node, getPos, editor }) => {
270
+ let updateImage = (img, node) => {
271
+ img.draggable = false;
272
+ img.src = node.attrs.src;
273
+ img.style.width = node.attrs.width ? `${node.attrs.width}px` : '';
274
+ if (node.attrs.float === 'left' || node.attrs.float === 'right') {
275
+ img.style.display = 'block';
276
+ if (node.attrs.float === 'left')
277
+ img.style.margin = "0 10px 0 0";
278
+ else if (node.attrs.float === 'right')
279
+ img.style.margin = "0 0 0 10px";
287
280
  }
288
- },
289
- placeholder: 'Scrivi o incolla il tuo contenuto qui!'
281
+ else {
282
+ img.style.display = 'inline-block';
283
+ img.style.verticalAlign = 'middle';
284
+ }
285
+ };
286
+ let updateWrapper = (wrapper, node) => {
287
+ wrapper.draggable = true;
288
+ wrapper.contentEditable = "false";
289
+ wrapper.className = `img-wrapper`;
290
+ wrapper.style.float = node.attrs.float;
291
+ if (node.attrs.float === 'left' || node.attrs.float === 'right') {
292
+ wrapper.style.width = "auto";
293
+ wrapper.style.textAlign = "auto";
294
+ }
295
+ else {
296
+ wrapper.style.width = "100%";
297
+ wrapper.style.textAlign = "center";
298
+ }
299
+ };
300
+ const wrapper = document.createElement('div');
301
+ updateWrapper(wrapper, node);
302
+ const img = document.createElement('img');
303
+ updateImage(img, node);
304
+ const toolbar = document.createElement('div');
305
+ toolbar.className = "img-toolbar";
306
+ const btn = (t, fn) => {
307
+ const b = document.createElement('button');
308
+ b.textContent = t;
309
+ b.onclick = fn;
310
+ return b;
311
+ };
312
+ toolbar.append(btn('Centro', () => editor.view.dispatch(editor.view.state.tr.setNodeMarkup(getPos(), undefined, { ...node.attrs, ...{ float: 'none' } }))), btn('Sinistra', () => editor.view.dispatch(editor.view.state.tr.setNodeMarkup(getPos(), undefined, { ...node.attrs, ...{ float: 'left' } }))), btn('Destra', () => editor.view.dispatch(editor.view.state.tr.setNodeMarkup(getPos(), undefined, { ...node.attrs, ...{ float: 'right' } }))), btn('Elimina', () => {
313
+ const nodePos = getPos();
314
+ editor.view.dispatch(editor.view.state.tr.delete(nodePos, nodePos + node.nodeSize));
315
+ }));
316
+ // resize
317
+ let startX = 0;
318
+ let startW = 0;
319
+ const handle = document.createElement('div');
320
+ handle.className = "img-resize-handle";
321
+ handle.onmousedown = (e) => {
322
+ startX = e.clientX;
323
+ startW = img.offsetWidth;
324
+ let latestWidth = null;
325
+ let ticking = false;
326
+ const move = (e) => {
327
+ latestWidth = Math.max(80, startW + (e.clientX - startX));
328
+ if (!ticking) {
329
+ ticking = true;
330
+ requestAnimationFrame(() => {
331
+ const nodePos = getPos();
332
+ editor.view.dispatch(editor.view.state.tr.setNodeMarkup(nodePos, undefined, { ...node.attrs, width: latestWidth }));
333
+ ticking = false;
334
+ });
335
+ }
336
+ };
337
+ const up = () => {
338
+ window.removeEventListener('mousemove', move);
339
+ window.removeEventListener('mouseup', up);
340
+ };
341
+ window.addEventListener('mousemove', move);
342
+ window.addEventListener('mouseup', up);
343
+ // Per evitare che si attivi il drag dell'elemento sotto. Dal resizer si usa solo il resizer
344
+ e.preventDefault();
345
+ };
346
+ wrapper.append(img, toolbar, handle);
347
+ return {
348
+ dom: wrapper,
349
+ destroy() { wrapper.remove(); },
350
+ update(updatedNode) {
351
+ // different node type -> recreate NodeView
352
+ if (updatedNode.type !== node.type)
353
+ return false;
354
+ node = updatedNode;
355
+ updateImage(img, node);
356
+ updateWrapper(wrapper, node);
357
+ return true;
358
+ },
359
+ };
290
360
  };
291
- }
292
- onEditorChange({ editor }) {
293
- let data = editor?.getData();
294
- if (data !== undefined)
295
- this.propagateChange(data);
296
- }
297
- registerOnChange(fn) {
298
- this.propagateChange = fn;
299
- }
300
- registerOnTouched(fn) {
301
- this.onTouched = fn;
302
- }
303
- writeValue(obj) {
304
- if (obj !== undefined) {
305
- this.value = obj;
306
- }
307
- else
308
- this.value = "";
309
- }
310
- validate(c) {
311
- if (this.value || this.Readonly || !this.Required)
312
- return null;
313
- return null;
314
- }
315
- changed() {
316
- this.propagateChange(this.value);
317
- }
318
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: HtmlEditorStandaloneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
319
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.15", type: HtmlEditorStandaloneComponent, isStandalone: true, selector: "html-editor-standalone", inputs: { PersistentEditor: "PersistentEditor", Readonly: "Readonly", Required: "Required", Validation: "Validation" }, outputs: { onEditorLoaded: "onEditorLoaded" }, providers: [
320
- { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => HtmlEditorStandaloneComponent), multi: true },
321
- { provide: NG_VALIDATORS, useExisting: forwardRef(() => HtmlEditorStandaloneComponent), multi: true }
322
- ], ngImport: i0, template: "<div class=\"html-editor\">\r\n <ckeditor *ngIf=\"Editor && EditorConfig\"\r\n [data]=\"value\"\r\n [editor]=\"Editor\"\r\n [config]=\"EditorConfig\"\r\n (change)=\"onEditorChange($event)\">\r\n </ckeditor>\r\n <div *ngIf=\"!Editor && !EditorConfig\">\r\n Editor in caricamento...\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap\";:root{--ck-content-font-family: \"Lato\"}@media print{body{margin:0!important}}.main-container{font-family:var(--ck-content-font-family);width:fit-content;margin-left:auto;margin-right:auto}.editor-container_classic-editor .editor-container__editor{min-width:795px;max-width:795px}.html-editor .ck-content{overflow-x:hidden!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i2.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }], encapsulation: i0.ViewEncapsulation.None }); }
323
- }
324
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: HtmlEditorStandaloneComponent, decorators: [{
325
- type: Component,
326
- args: [{ selector: "html-editor-standalone", standalone: true, imports: [CommonModule, FormsModule, CKEditorModule], encapsulation: ViewEncapsulation.None, providers: [
327
- { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => HtmlEditorStandaloneComponent), multi: true },
328
- { provide: NG_VALIDATORS, useExisting: forwardRef(() => HtmlEditorStandaloneComponent), multi: true }
329
- ], template: "<div class=\"html-editor\">\r\n <ckeditor *ngIf=\"Editor && EditorConfig\"\r\n [data]=\"value\"\r\n [editor]=\"Editor\"\r\n [config]=\"EditorConfig\"\r\n (change)=\"onEditorChange($event)\">\r\n </ckeditor>\r\n <div *ngIf=\"!Editor && !EditorConfig\">\r\n Editor in caricamento...\r\n </div>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap\";:root{--ck-content-font-family: \"Lato\"}@media print{body{margin:0!important}}.main-container{font-family:var(--ck-content-font-family);width:fit-content;margin-left:auto;margin-right:auto}.editor-container_classic-editor .editor-container__editor{min-width:795px;max-width:795px}.html-editor .ck-content{overflow-x:hidden!important}\n"] }]
330
- }], propDecorators: { PersistentEditor: [{
331
- type: Input
332
- }], Readonly: [{
333
- type: Input
334
- }], Required: [{
335
- type: Input
336
- }], Validation: [{
337
- type: Input
338
- }], onEditorLoaded: [{
339
- type: Output
340
- }] } });
361
+ },
362
+ });
341
363
 
342
- const cloudConfig$1 = {
343
- version: '46.1.1',
344
- translations: ['it']
345
- };
346
- const LICENSE_KEY$1 = 'eyJhbGciOiJFUzI1NiJ9.eyJleHAiOjE4MDQ5ODIzOTksImp0aSI6ImJkZDU4ZGM3LTlhYzgtNDA4Mi05ZDk3LTg3Yzc0NjEzMDlmNyIsInVzYWdlRW5kcG9pbnQiOiJodHRwczovL3Byb3h5LWV2ZW50LmNrZWRpdG9yLmNvbSIsImRpc3RyaWJ1dGlvbkNoYW5uZWwiOlsiY2xvdWQiLCJkcnVwYWwiXSwid2hpdGVMYWJlbCI6dHJ1ZSwiZmVhdHVyZXMiOlsiRFJVUCIsIkRPIiwiRlAiLCJTQyIsIlRPQyIsIlRQTCIsIlBPRSIsIkNDIiwiTUYiLCJFMlAiLCJFMlciLCJNTEwiLCJTRUUiLCJFQ0giLCJFSVMiLCJMSCIsIkZPTyJdLCJ2YyI6IjdjZjg2OGYwIn0.vw-04yWGiTzZVSaGxQUGCxWXBUQXRJ0yB2ItqwvNnd7nEjMFC4d9kiDgbkzGnHgAjkiQEUqTzrM7b1z4cR6rhA';
347
- class MarkdownEditorStandaloneComponent {
364
+ class EditorComponent {
348
365
  constructor() {
349
- this.Readonly = false; // Angular19 -> [disabled]
350
- this.Required = false;
351
- this.Validation = false;
352
- // Mention
353
- this.MentionMarker = "@";
354
- this.MentionEntities = [];
355
- this.onEditorLoaded = new EventEmitter();
356
- //#region CKEditor5
357
- this.Editor = null;
358
- this.EditorConfig = null;
359
- this.EditorModel = { editorDataText: '', };
360
- //#endregion
361
- //#region Forms
366
+ this.onChange = (_) => { };
362
367
  this.onTouched = () => { };
363
- this.propagateChange = (_) => { };
368
+ this.element = viewChild.required("element");
369
+ this.EmojiSupport = input(false);
370
+ this.BulletListSupport = input(false);
371
+ this.TablesSupport = input(false);
372
+ this.Mode = input.required();
373
+ this.readonly = input(false, { transform: booleanAttribute });
374
+ this.disabled = input(false, { transform: booleanAttribute });
375
+ this.required = input(false, { transform: booleanAttribute });
376
+ this.MentionEntities = input([]);
377
+ this.name = input.required();
378
+ this.inputChange = new EventEmitter();
379
+ this.inputFocus = new EventEmitter();
380
+ this.inputFinalized = new EventEmitter();
381
+ }
382
+ registerOnChange(fn) { this.onChange = fn; }
383
+ registerOnTouched(fn) { this.onTouched = fn; }
384
+ validate(_) {
385
+ return null;
364
386
  }
365
- get mentionEntities() {
366
- return this.MentionEntities.map(m => {
367
- return { id: this.MentionMarker + m.id, description: m.description };
387
+ ngOnInit() {
388
+ let config = [];
389
+ config.push(...[
390
+ StarterKit,
391
+ TextStyle,
392
+ Color,
393
+ Underline,
394
+ FontSize,
395
+ FontFamily,
396
+ LineHeight,
397
+ TextAlign.configure({ types: ['heading', 'paragraph'] })
398
+ ]);
399
+ if (this.Mode() == "markdown")
400
+ config.push(Markdown);
401
+ if (this.Mode() == "html") {
402
+ if (this.TablesSupport())
403
+ config.push(...[Table.configure({ lastColumnResizable: true, resizable: true }), TableRow, TableHeader, TableCell]);
404
+ config.push(CustomImageWithView);
405
+ }
406
+ if ((this.MentionEntities()?.length || 0) > 0)
407
+ config.push(Mention.configure({
408
+ deleteTriggerWithBackspace: true,
409
+ HTMLAttributes: { class: 'mention', },
410
+ renderText({ options, node }) { return `${options.suggestion.char}${node.attrs.description ?? node.attrs.id}`; },
411
+ suggestion: Suggestion(this.MentionEntities()),
412
+ }));
413
+ this.EditorRef = new Editor({
414
+ element: this.element().nativeElement,
415
+ extensions: config,
416
+ editable: !this.readonly(),
417
+ onUpdate: (_) => {
418
+ let value = "";
419
+ switch (this.Mode()) {
420
+ case "html":
421
+ value = this.EditorRef?.getHTML() || "";
422
+ // Emissione del valore vuoto trimmato per bene
423
+ if (value.trim() === "<p></p>")
424
+ value = "";
425
+ break;
426
+ case "markdown":
427
+ value = this.EditorRef?.getMarkdown() || "";
428
+ break;
429
+ case "text":
430
+ value = this.EditorRef?.getText() || "";
431
+ break;
432
+ }
433
+ this.onChange(value);
434
+ }
368
435
  });
369
436
  }
370
- initializeEditor() {
371
- if (!this.Editor) {
372
- loadCKEditorCloud(cloudConfig$1).then(this.internalInitializeEditor.bind(this));
373
- this.onEditorLoaded.emit();
437
+ async writeValue(obj) {
438
+ // Coercizione di tutti i valori "vuoti" all'unico "null"
439
+ if (obj === null || obj === undefined)
440
+ obj = null;
441
+ this.value = obj;
442
+ this.EditorRef?.commands.clearContent();
443
+ if (this.Mode() === "markdown")
444
+ this.EditorRef?.commands.setContent(this.value, { contentType: "markdown" });
445
+ else
446
+ this.EditorRef?.commands.insertContent(this.value);
447
+ }
448
+ setTypography(selectElement) {
449
+ switch (selectElement.target.value) {
450
+ case "paragraph":
451
+ this.EditorRef?.chain().focus().setParagraph().run();
452
+ break;
453
+ default:
454
+ this.EditorRef?.chain().focus().setHeading({ level: parseInt(selectElement.target.value.charAt(1)) }).run();
455
+ break;
374
456
  }
457
+ selectElement.target.value = "";
375
458
  }
376
- ngOnDestroy() {
377
- console.warn("Il componente MarkdownEditorComponent è stato distrutto");
459
+ setBold() {
460
+ this.EditorRef?.chain().focus().toggleBold().run();
378
461
  }
379
- internalInitializeEditor(cloudConfig) {
380
- const { ClassicEditor,
381
- /* Plugins */
382
- Alignment, AutoImage, Autosave, Base64UploadAdapter, Bold, CloudServices, Essentials, Heading, ImageInline, ImageBlock, ImageInsert, ImageInsertViaUrl, ImageResize, ImageStyle, ImageToolbar, ImageUpload, Italic, Link, List, Paragraph, PasteFromOffice, Mention, Emoji, Markdown, PasteFromMarkdownExperimental } = cloudConfig.CKEditor;
383
- this.Editor = ClassicEditor;
384
- this.EditorConfig = {
385
- toolbar: {
386
- items: ['heading', '|', 'bold', 'italic', 'bulletedList', '|', 'undo', 'redo', '|', 'emoji'],
387
- shouldNotGroupWhenFull: false
388
- },
389
- plugins: [
390
- Alignment, AutoImage, Autosave, Base64UploadAdapter, Bold, CloudServices, Essentials, Heading,
391
- ImageInline, ImageBlock, ImageInsert, ImageInsertViaUrl, ImageResize, ImageStyle, ImageToolbar, ImageUpload, Italic,
392
- Link, List, Paragraph, PasteFromOffice, Mention, Emoji, Markdown, PasteFromMarkdownExperimental
393
- ],
394
- heading: {
395
- options: [
396
- { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
397
- { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
398
- { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
399
- { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
400
- { model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' },
401
- { model: 'heading5', view: 'h5', title: 'Heading 5', class: 'ck-heading_heading5' },
402
- { model: 'heading6', view: 'h6', title: 'Heading 6', class: 'ck-heading_heading6' }
403
- ]
404
- },
405
- htmlSupport: {
406
- allow: [{ name: /^.*$/, styles: true, attributes: true, classes: true }]
407
- },
408
- image: {
409
- toolbar: ['imageStyle:block', 'imageStyle:side', '|', 'resizeImage:original'],
410
- insert: { type: 'auto' }
411
- },
412
- initialData: "",
413
- language: 'it',
414
- licenseKey: LICENSE_KEY$1,
415
- link: {
416
- addTargetToExternalLinks: true,
417
- defaultProtocol: 'https://',
418
- decorators: {
419
- toggleDownloadable: {
420
- mode: 'manual',
421
- label: 'Downloadable',
422
- attributes: { download: 'file' }
423
- }
424
- }
425
- },
426
- placeholder: 'Scrivi o incolla il tuo contenuto qui!'
427
- };
428
- // Attivo le Mention se sono presenti "MentionEntities"
429
- // "Mention" serve sempre per "Emoji"
430
- if (this.MentionEntities?.length > 0) {
431
- this.EditorConfig.mention = {
432
- feeds: [
433
- {
434
- marker: this.MentionMarker,
435
- feed: (filter) => {
436
- return this.mentionEntities.filter(t => t.id.toLowerCase().includes(filter.toLowerCase()));
437
- },
438
- minimumCharacters: 1,
439
- // Callback per stile
440
- itemRenderer: (item) => {
441
- let dropdownElement = document.createElement('div');
442
- dropdownElement.classList.add("ck-mention-dropdown");
443
- dropdownElement.id = `mention-list-item-id-${item.id}`;
444
- let useridElement = document.createElement('div');
445
- useridElement.classList.add("ck-username-id");
446
- useridElement.textContent = `${item.id.substr(1, item.id.length)}`;
447
- let userdescElement = document.createElement('div');
448
- userdescElement.textContent = ` ● ${item.description}`;
449
- dropdownElement.appendChild(useridElement);
450
- dropdownElement.appendChild(userdescElement);
451
- return dropdownElement;
452
- }
453
- },
454
- ]
455
- };
456
- }
462
+ setItalic() {
463
+ this.EditorRef?.chain().focus().toggleItalic().run();
457
464
  }
458
- onEditorChange({ editor }) {
459
- let data = editor?.getData();
460
- if (data !== undefined)
461
- this.propagateChange(data);
465
+ setUnderlined() {
466
+ this.EditorRef?.chain().focus().toggleUnderline().run();
462
467
  }
463
- registerOnChange(fn) {
464
- this.propagateChange = fn;
468
+ setBulletList() {
469
+ this.EditorRef?.chain().focus().toggleBulletList().run();
465
470
  }
466
- registerOnTouched(fn) {
467
- this.onTouched = fn;
471
+ undoAction() {
472
+ this.EditorRef?.chain().focus().undo().run();
468
473
  }
469
- writeValue(obj) {
470
- if (obj !== undefined) {
471
- this.value = obj;
472
- }
473
- else
474
- this.value = "";
474
+ redoAction() {
475
+ this.EditorRef?.chain().focus().redo().run();
475
476
  }
476
- validate(c) {
477
- if (this.value || this.Readonly || !this.Required)
478
- return null;
479
- return null;
477
+ addTable() {
478
+ this.EditorRef?.chain().focus().insertTable({ rows: 1, cols: 2, withHeaderRow: false }).run();
480
479
  }
481
- changed() {
482
- this.propagateChange(this.value);
480
+ deleteTable() {
481
+ this.EditorRef?.chain().focus().deleteTable().run();
483
482
  }
484
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MarkdownEditorStandaloneComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
485
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.15", type: MarkdownEditorStandaloneComponent, isStandalone: true, selector: "markdown-editor-standalone", inputs: { Readonly: "Readonly", Required: "Required", Validation: "Validation", MentionMarker: "MentionMarker", MentionEntities: "MentionEntities", PersistentEditor: "PersistentEditor" }, outputs: { onEditorLoaded: "onEditorLoaded" }, providers: [
486
- {
487
- provide: NG_VALUE_ACCESSOR,
488
- useExisting: forwardRef(() => MarkdownEditorStandaloneComponent),
489
- multi: true
490
- },
491
- {
492
- provide: NG_VALIDATORS,
493
- useExisting: forwardRef(() => MarkdownEditorStandaloneComponent),
494
- multi: true
495
- }
496
- ], ngImport: i0, template: "<div class=\"markdown-editor\">\r\n <ckeditor *ngIf=\"Editor && EditorConfig\"\r\n [data]=\"value\"\r\n [editor]=\"Editor\"\r\n [config]=\"EditorConfig\"\r\n (change)=\"onEditorChange($event)\">\r\n </ckeditor>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap\";:root{--ck-content-font-family: \"Lato\"}@media print{body{margin:0!important}}.main-container{font-family:var(--ck-content-font-family);width:fit-content;margin-left:auto;margin-right:auto}.editor-container_classic-editor .editor-container__editor{min-width:795px;max-width:795px}.markdown-editor .ck-content{overflow-x:hidden!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i2.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }], encapsulation: i0.ViewEncapsulation.None }); }
497
- }
498
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: MarkdownEditorStandaloneComponent, decorators: [{
499
- type: Component,
500
- args: [{ selector: "markdown-editor-standalone", standalone: true, imports: [CommonModule, FormsModule, CKEditorModule], encapsulation: ViewEncapsulation.None, providers: [
501
- {
502
- provide: NG_VALUE_ACCESSOR,
503
- useExisting: forwardRef(() => MarkdownEditorStandaloneComponent),
504
- multi: true
505
- },
506
- {
507
- provide: NG_VALIDATORS,
508
- useExisting: forwardRef(() => MarkdownEditorStandaloneComponent),
509
- multi: true
510
- }
511
- ], template: "<div class=\"markdown-editor\">\r\n <ckeditor *ngIf=\"Editor && EditorConfig\"\r\n [data]=\"value\"\r\n [editor]=\"Editor\"\r\n [config]=\"EditorConfig\"\r\n (change)=\"onEditorChange($event)\">\r\n </ckeditor>\r\n</div>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap\";:root{--ck-content-font-family: \"Lato\"}@media print{body{margin:0!important}}.main-container{font-family:var(--ck-content-font-family);width:fit-content;margin-left:auto;margin-right:auto}.editor-container_classic-editor .editor-container__editor{min-width:795px;max-width:795px}.markdown-editor .ck-content{overflow-x:hidden!important}\n"] }]
512
- }], propDecorators: { Readonly: [{
513
- type: Input
514
- }], Required: [{
515
- type: Input
516
- }], Validation: [{
517
- type: Input
518
- }], MentionMarker: [{
519
- type: Input
520
- }], MentionEntities: [{
521
- type: Input
522
- }], onEditorLoaded: [{
523
- type: Output
524
- }], PersistentEditor: [{
525
- type: Input
526
- }] } });
527
-
528
- const cloudConfig = {
529
- version: '46.1.1',
530
- translations: ['it']
531
- };
532
- const LICENSE_KEY = 'eyJhbGciOiJFUzI1NiJ9.eyJleHAiOjE4MDQ5ODIzOTksImp0aSI6ImJkZDU4ZGM3LTlhYzgtNDA4Mi05ZDk3LTg3Yzc0NjEzMDlmNyIsInVzYWdlRW5kcG9pbnQiOiJodHRwczovL3Byb3h5LWV2ZW50LmNrZWRpdG9yLmNvbSIsImRpc3RyaWJ1dGlvbkNoYW5uZWwiOlsiY2xvdWQiLCJkcnVwYWwiXSwid2hpdGVMYWJlbCI6dHJ1ZSwiZmVhdHVyZXMiOlsiRFJVUCIsIkRPIiwiRlAiLCJTQyIsIlRPQyIsIlRQTCIsIlBPRSIsIkNDIiwiTUYiLCJFMlAiLCJFMlciLCJNTEwiLCJTRUUiLCJFQ0giLCJFSVMiLCJMSCIsIkZPTyJdLCJ2YyI6IjdjZjg2OGYwIn0.vw-04yWGiTzZVSaGxQUGCxWXBUQXRJ0yB2ItqwvNnd7nEjMFC4d9kiDgbkzGnHgAjkiQEUqTzrM7b1z4cR6rhA';
533
- class PersistentEditor {
534
- constructor() {
535
- this.Mode = input.required();
536
- this.EditorTemplate = viewChild("template");
537
- this.DEBUG = inject(TXE_DEBUG, { optional: true }) || false;
538
- this.OnEditorLoadedCallbackFunction = null;
539
- this.Editor = null;
540
- this.EditorConfig = null;
541
- this.MentionMarker = "@";
542
- this.MentionEntities = [];
483
+ addRow() {
484
+ this.EditorRef?.chain().focus().addRowAfter().run();
543
485
  }
544
- iInitializeEditor(mentionmarker, mentionentitis, onEditorLoaded) {
545
- if (onEditorLoaded)
546
- this.OnEditorLoadedCallbackFunction = onEditorLoaded;
547
- if (this.Editor && this.EditorConfig) {
548
- if (this.DEBUG)
549
- console.log("@ Persistent Editor @ Editor is already initialized");
550
- }
551
- else {
552
- loadCKEditorCloud(cloudConfig).then(this.internalInitializeEditor.bind(this));
553
- if (this.DEBUG)
554
- console.log(`@ Persistent Editor @ Editor is initialized (${this.Mode()} mode). loadCKEditorCloud has been called`);
555
- this.OnEditorLoadedCallbackFunction?.("initialize");
556
- }
557
- this.MentionMarker = mentionmarker ?? "@";
558
- this.MentionEntities = mentionentitis ?? [];
559
- if ((this.MentionEntities?.length || 0) > 0) {
560
- if (this.DEBUG)
561
- console.log(`@ Persistent Editor @ Editor is using ${this.MentionEntities?.length} mentions possibilities`);
562
- }
486
+ deleteRow() {
487
+ this.EditorRef?.chain().focus().deleteRow().run();
563
488
  }
564
- onReady() {
565
- this.OnEditorLoadedCallbackFunction?.("ready");
489
+ addColumn() {
490
+ this.EditorRef?.chain().focus().addColumnAfter().run();
566
491
  }
567
- internalInitializeEditor(cloudConfig) {
568
- const { ClassicEditor,
569
- // Plugin generali
570
- Alignment, AutoImage, Autosave, Base64UploadAdapter, Bold, CloudServices, Essentials, Heading, ImageInline, ImageBlock, ImageInsert, ImageInsertViaUrl, ImageResize, ImageStyle, ImageToolbar, ImageUpload, Italic, Link, List, Paragraph, PasteFromOffice,
571
- // Plugin specifici modalità HTML
572
- GeneralHtmlSupport, Underline, SourceEditing,
573
- // Plugin specifici modalità Markdown
574
- Mention, Emoji, Markdown, PasteFromMarkdownExperimental } = cloudConfig.CKEditor;
575
- let markdownPlugins = [Mention, Emoji, Markdown, PasteFromMarkdownExperimental];
576
- let htmlPlugins = [GeneralHtmlSupport, Underline, SourceEditing];
577
- this.Editor = ClassicEditor;
578
- this.EditorConfig = {
579
- toolbar: {
580
- items: this.Mode() == "html" ?
581
- ['heading', '|', 'bold', 'italic', 'underline', '|', 'undo', 'redo', '|', 'insertImage', 'imageStyle:block', 'imageStyle:alignLeft', 'imageStyle:alignRight', '|', 'sourceEditing'] :
582
- ['heading', '|', 'bold', 'italic', 'bulletedList', '|', 'undo', 'redo', '|', 'emoji'],
583
- shouldNotGroupWhenFull: false
584
- },
585
- plugins: [
586
- Alignment, AutoImage, Autosave, Base64UploadAdapter, Bold, CloudServices, Essentials,
587
- Heading, ImageInline, ImageBlock, ImageInsert, ImageInsertViaUrl, ImageResize, ImageStyle, ImageToolbar, ImageUpload, Italic,
588
- Link, List, Paragraph, PasteFromOffice,
589
- ...(this.Mode() == "html" ? htmlPlugins : markdownPlugins)
590
- ],
591
- heading: {
592
- options: [
593
- { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
594
- { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
595
- { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
596
- { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
597
- { model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' },
598
- { model: 'heading5', view: 'h5', title: 'Heading 5', class: 'ck-heading_heading5' },
599
- { model: 'heading6', view: 'h6', title: 'Heading 6', class: 'ck-heading_heading6' }
600
- ]
601
- },
602
- htmlSupport: {
603
- allow: [{ name: /^.*$/, styles: true, attributes: true, classes: true }]
604
- },
605
- image: {
606
- toolbar: ['imageStyle:block', 'imageStyle:side', '|', 'resizeImage:original'],
607
- insert: { type: 'auto' }
608
- },
609
- initialData: "",
610
- language: 'it',
611
- licenseKey: LICENSE_KEY,
612
- link: {
613
- addTargetToExternalLinks: true,
614
- defaultProtocol: 'https://',
615
- decorators: {
616
- toggleDownloadable: {
617
- mode: 'manual',
618
- label: 'Downloadable',
619
- attributes: { download: 'file' }
620
- }
621
- }
622
- },
623
- placeholder: 'Scrivi o incolla il tuo contenuto qui!'
624
- };
625
- if (this.Mode() == "markdown")
626
- this.setupMentions();
492
+ deleteColumn() {
493
+ this.EditorRef?.chain().focus().deleteColumn().run();
627
494
  }
628
- setupMentions() {
629
- if (!this.EditorConfig)
630
- return;
631
- this.EditorConfig.mention = {
632
- feeds: [
633
- {
634
- marker: this.MentionMarker ?? "@",
635
- feed: (filter) => {
636
- return this.MentionEntities?.filter(t => t.id.toLowerCase().includes(filter.toLowerCase())) || [];
637
- },
638
- minimumCharacters: 1,
639
- // Callback per stile
640
- itemRenderer: (item) => {
641
- let dropdownElement = document.createElement('div');
642
- dropdownElement.classList.add("ck-mention-dropdown");
643
- dropdownElement.id = `mention-list-item-id-${item.id}`;
644
- let useridElement = document.createElement('div');
645
- useridElement.classList.add("ck-username-id");
646
- useridElement.textContent = `${item.id.substr(1, item.id.length)}`;
647
- let userdescElement = document.createElement('div');
648
- userdescElement.textContent = ` ● ${item.description}`;
649
- dropdownElement.appendChild(useridElement);
650
- dropdownElement.appendChild(userdescElement);
651
- return dropdownElement;
652
- }
653
- },
654
- ]
655
- };
495
+ setEmoji(selectElement) {
496
+ this.EditorRef?.chain().focus().insertContent(selectElement.target.value).run();
497
+ selectElement.target.value = "";
656
498
  }
657
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: PersistentEditor, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
658
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "19.2.15", type: PersistentEditor, isStandalone: true, selector: "persistent-ckeditor", inputs: { Mode: { classPropertyName: "Mode", publicName: "Mode", isSignal: true, isRequired: true, transformFunction: null } }, viewQueries: [{ propertyName: "EditorTemplate", first: true, predicate: ["template"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template #template let-EditorModel>\r\n <ckeditor \r\n *ngIf=\"Editor && EditorConfig\" \r\n [editor]=\"Editor\" \r\n [config]=\"EditorConfig\" \r\n [data]=\"EditorModel.value\" \r\n (ready)=\"onReady()\"\r\n (change)=\"EditorModel.onEditorChange($event)\"></ckeditor>\r\n</ng-template>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap\";:root{--ck-content-font-family: \"Lato\"}@media print{body{margin:0!important}}.main-container{font-family:var(--ck-content-font-family);width:fit-content;margin-left:auto;margin-right:auto}.editor-container_classic-editor .editor-container__editor{min-width:795px;max-width:795px}.html-editor .ck-content{overflow-x:hidden!important}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i2.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }], encapsulation: i0.ViewEncapsulation.None }); }
499
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: EditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
500
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.15", type: EditorComponent, isStandalone: true, selector: "editor", inputs: { EmojiSupport: { classPropertyName: "EmojiSupport", publicName: "EmojiSupport", isSignal: true, isRequired: false, transformFunction: null }, BulletListSupport: { classPropertyName: "BulletListSupport", publicName: "BulletListSupport", isSignal: true, isRequired: false, transformFunction: null }, TablesSupport: { classPropertyName: "TablesSupport", publicName: "TablesSupport", isSignal: true, isRequired: false, transformFunction: null }, Mode: { classPropertyName: "Mode", publicName: "Mode", isSignal: true, isRequired: true, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, MentionEntities: { classPropertyName: "MentionEntities", publicName: "MentionEntities", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { inputChange: "inputChange", inputFocus: "inputFocus", inputFinalized: "inputFinalized" }, providers: [
501
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EditorComponent), multi: true },
502
+ { provide: NG_VALIDATORS, useExisting: forwardRef(() => EditorComponent), multi: true }
503
+ ], viewQueries: [{ propertyName: "element", first: true, predicate: ["element"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"editor-shell\">\r\n <div class=\"editor-toolbar\">\r\n\r\n <!-- Typography -->\r\n <select id=\"heading-select\" (change)=\"setTypography($event)\">\r\n <option value=\"\" selected hidden>Pragrafo</option>\r\n <option value=\"paragraph\">Pragrafo</option>\r\n <option value=\"h1\">Intestazione 1</option>\r\n <option value=\"h2\">Intestazione 2</option>\r\n <option value=\"h3\">Intestazione 3</option>\r\n <option value=\"h4\">Intestazione 4</option>\r\n <option value=\"h5\">Intestazione 5</option>\r\n <option value=\"h6\">Intestazione 6</option>\r\n </select>\r\n\r\n <div class=\"divider\"></div>\r\n\r\n <!-- Formatting -->\r\n <button (click)=\"setBold()\"><b>B</b></button>\r\n <button (click)=\"setItalic()\"><i>I</i></button>\r\n <button (click)=\"setUnderlined()\"><u>U</u></button>\r\n\r\n <!-- Lists -->\r\n @if(BulletListSupport()){\r\n <button (click)=\"setBulletList()\">\u2022 Lista</button>\r\n }\r\n\r\n <div class=\"divider\"></div>\r\n\r\n @if(Mode() == \"html\" && TablesSupport())\r\n {\r\n <button (click)=\"addTable()\">\u229E<span style=\"font-size: 10px;\">\u271A</span></button>\r\n <button (click)=\"deleteTable()\">\u229E<span style=\"font-size: 10px;\">\u25AC</span></button>\r\n <button (click)=\"addRow()\">\uD83D\uDDA8<span style=\"font-size: 10px;\">\u271A</span></button>\r\n <button (click)=\"deleteRow()\">\uD83D\uDDA8<span style=\"font-size: 10px;\">\u25AC</span></button>\r\n <button (click)=\"addColumn()\">\uD83D\uDDC4<span style=\"font-size: 10px;\">\u271A</span></button>\r\n <button (click)=\"deleteColumn()\">\uD83D\uDDC4<span style=\"font-size: 10px;\">\u25AC</span></button>\r\n <div class=\"divider\"></div>\r\n }\r\n\r\n <!-- History -->\r\n <button (click)=\"undoAction()\">\u21B6</button>\r\n <button (click)=\"redoAction()\">\u21B7</button>\r\n\r\n @if(EmojiSupport()){\r\n <select id=\"emoji-select\" (change)=\"setEmoji($event)\">\r\n <option value=\"\" hidden selected>\uD83D\uDE0A</option>\r\n <option value=\"\uD83D\uDE0A\">\uD83D\uDE0A</option>\r\n <option value=\"\uD83D\uDE00\">\uD83D\uDE00</option>\r\n <option value=\"\uD83D\uDE02\">\uD83D\uDE02</option>\r\n <option value=\"\uD83D\uDD25\">\uD83D\uDD25</option>\r\n <option value=\"\uD83C\uDF89\">\uD83C\uDF89</option>\r\n <option value=\"\uD83D\uDE80\">\uD83D\uDE80</option>\r\n <option value=\"\u2764\uFE0F\">\u2764\uFE0F</option>\r\n <option value=\"\uD83D\uDC4D\">\uD83D\uDC4D</option>\r\n </select>\r\n }\r\n </div>\r\n\r\n <div #element></div>\r\n</div>", styles: [".editor-shell{border:1px solid #dcdcdc;border-radius:10px;background:#fff;overflow:hidden;isolation:isolate;box-shadow:0 1px 2px #0000000a,0 6px 24px #0000000d;font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.editor-toolbar{display:flex;align-items:center;gap:4px;padding:4px;background:linear-gradient(to bottom,#fafafa,#f3f3f3);border-bottom:1px solid #e5e5e5;flex-wrap:wrap}.editor-toolbar button{display:inline-flex;align-items:center;justify-content:center;line-height:1;min-width:36px;height:36px;padding:0 12px;border:1px solid #ddd;border-radius:8px;background:transparent;color:#333;font-size:18px;font-weight:500;cursor:pointer;transition:background .12s ease,border-color .12s ease,color .12s ease}.editor-toolbar button:hover{background:#e9e9e9}.editor-toolbar button:active{transform:translateY(1px)}.editor-toolbar button.is-active{background:#dfe7ff;border-color:#c7d2fe;color:#2342c0}.editor-toolbar select{height:36px;padding:0 12px;border:1px solid #d6d6d6;border-radius:8px;background:#fff;font-size:14px;color:#222;cursor:pointer;transition:border-color .12s ease,box-shadow .12s ease,background .12s ease}.editor-toolbar select:hover{background:#fafafa}.editor-toolbar select:focus{outline:none;border-color:#8ba8ff;box-shadow:0 0 0 3px #6082ff26}.editor-toolbar .divider{width:1px;height:24px;background:#ddd;margin:0 2px}.ProseMirror{min-height:60px;padding:12px;outline:none;font-size:16px;line-height:1.7;color:#1f1f1f;caret-color:#2563eb}.ProseMirror h1,.ProseMirror h2,.ProseMirror h3{line-height:1.2;margin-top:.8em;margin-bottom:.4em;font-weight:700;letter-spacing:-.02em}.ProseMirror h1{font-size:2rem}.ProseMirror h2{font-size:1.5rem}.ProseMirror h3{font-size:1.25rem}.ProseMirror p{margin:0}.ProseMirror ul,.ProseMirror ol{padding-left:1.5rem;margin:5px 0}.ProseMirror li{margin:0}.ProseMirror blockquote{margin:1.4rem 0;padding-left:1rem;border-left:4px solid #c7d2fe;color:#555}.ProseMirror code{background:#f3f4f6;color:#d63384;padding:.15em .35em;border-radius:4px;font-size:.92em}.ProseMirror pre{background:#1f2937;color:#f9fafb;padding:16px 18px;border-radius:10px;overflow-x:auto;margin:1.2rem 0}.ProseMirror pre code{background:transparent;color:inherit;padding:0}.ProseMirror a{color:#2563eb;text-decoration:underline}.ProseMirror hr{border:none;border-top:1px solid #e2e2e2;margin:2rem 0}.ProseMirror table{width:100%;border-collapse:collapse;margin:1.2rem 0}.ProseMirror th,.ProseMirror td{border:1px solid #ddd;padding:5px;position:relative}.ProseMirror th{background:#f5f5f5;text-align:left}.ProseMirror .column-resize-handle{position:absolute;top:0;right:-2px;width:5px;height:100%;cursor:col-resize;background:transparent;z-index:20}.ProseMirror ::selection{background:#6082ff40}.suggestion-popup{display:flex;flex-direction:column;min-width:220px;padding:6px;background:#fff;border:1px solid #e5e5e5;border-radius:10px;box-shadow:0 12px 30px #00000014,0 2px 8px #0000000d;z-index:1000}.suggestion-popup button{border:none;background:transparent;border-radius:8px;padding:8px 10px;text-align:left;cursor:pointer;font-size:14px}.suggestion-popup button:hover,.suggestion-popup button.is-selected{background:#eef2ff}.mention{display:inline-flex;align-items:center;background:#eef2ff;color:#304ffe;padding:2px 6px;border-radius:5px;font-weight:500}.img-wrapper{position:relative;display:inline-block;margin:12px 0}.img-wrapper img{max-width:100%;border-radius:10px;transition:opacity .2s ease}.img-toolbar{position:absolute;top:8px;left:8px;display:flex;gap:6px;background:#fffffff2;border:1px solid #e5e5e5;border-radius:8px;padding:4px;box-shadow:0 8px 20px #00000014}.img-toolbar button{border:none;background:transparent;padding:4px 6px;cursor:pointer;font-size:12px}.img-toolbar button:hover{background:#eef2ff;border-radius:6px}.img-resize-handle{position:absolute;right:0;bottom:0;width:12px;height:12px;background:#2563eb;border-radius:3px;cursor:se-resize}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }], encapsulation: i0.ViewEncapsulation.None }); }
659
504
  }
660
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: PersistentEditor, decorators: [{
505
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImport: i0, type: EditorComponent, decorators: [{
661
506
  type: Component,
662
- args: [{ selector: "persistent-ckeditor", standalone: true, imports: [CommonModule, FormsModule, CKEditorModule], encapsulation: ViewEncapsulation.None, template: "<ng-template #template let-EditorModel>\r\n <ckeditor \r\n *ngIf=\"Editor && EditorConfig\" \r\n [editor]=\"Editor\" \r\n [config]=\"EditorConfig\" \r\n [data]=\"EditorModel.value\" \r\n (ready)=\"onReady()\"\r\n (change)=\"EditorModel.onEditorChange($event)\"></ckeditor>\r\n</ng-template>", styles: ["@import\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap\";:root{--ck-content-font-family: \"Lato\"}@media print{body{margin:0!important}}.main-container{font-family:var(--ck-content-font-family);width:fit-content;margin-left:auto;margin-right:auto}.editor-container_classic-editor .editor-container__editor{min-width:795px;max-width:795px}.html-editor .ck-content{overflow-x:hidden!important}\n"] }]
663
- }] });
507
+ args: [{ selector: "editor", standalone: true, imports: [CommonModule, FormsModule], encapsulation: ViewEncapsulation.None, providers: [
508
+ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => EditorComponent), multi: true },
509
+ { provide: NG_VALIDATORS, useExisting: forwardRef(() => EditorComponent), multi: true }
510
+ ], template: "<div class=\"editor-shell\">\r\n <div class=\"editor-toolbar\">\r\n\r\n <!-- Typography -->\r\n <select id=\"heading-select\" (change)=\"setTypography($event)\">\r\n <option value=\"\" selected hidden>Pragrafo</option>\r\n <option value=\"paragraph\">Pragrafo</option>\r\n <option value=\"h1\">Intestazione 1</option>\r\n <option value=\"h2\">Intestazione 2</option>\r\n <option value=\"h3\">Intestazione 3</option>\r\n <option value=\"h4\">Intestazione 4</option>\r\n <option value=\"h5\">Intestazione 5</option>\r\n <option value=\"h6\">Intestazione 6</option>\r\n </select>\r\n\r\n <div class=\"divider\"></div>\r\n\r\n <!-- Formatting -->\r\n <button (click)=\"setBold()\"><b>B</b></button>\r\n <button (click)=\"setItalic()\"><i>I</i></button>\r\n <button (click)=\"setUnderlined()\"><u>U</u></button>\r\n\r\n <!-- Lists -->\r\n @if(BulletListSupport()){\r\n <button (click)=\"setBulletList()\">\u2022 Lista</button>\r\n }\r\n\r\n <div class=\"divider\"></div>\r\n\r\n @if(Mode() == \"html\" && TablesSupport())\r\n {\r\n <button (click)=\"addTable()\">\u229E<span style=\"font-size: 10px;\">\u271A</span></button>\r\n <button (click)=\"deleteTable()\">\u229E<span style=\"font-size: 10px;\">\u25AC</span></button>\r\n <button (click)=\"addRow()\">\uD83D\uDDA8<span style=\"font-size: 10px;\">\u271A</span></button>\r\n <button (click)=\"deleteRow()\">\uD83D\uDDA8<span style=\"font-size: 10px;\">\u25AC</span></button>\r\n <button (click)=\"addColumn()\">\uD83D\uDDC4<span style=\"font-size: 10px;\">\u271A</span></button>\r\n <button (click)=\"deleteColumn()\">\uD83D\uDDC4<span style=\"font-size: 10px;\">\u25AC</span></button>\r\n <div class=\"divider\"></div>\r\n }\r\n\r\n <!-- History -->\r\n <button (click)=\"undoAction()\">\u21B6</button>\r\n <button (click)=\"redoAction()\">\u21B7</button>\r\n\r\n @if(EmojiSupport()){\r\n <select id=\"emoji-select\" (change)=\"setEmoji($event)\">\r\n <option value=\"\" hidden selected>\uD83D\uDE0A</option>\r\n <option value=\"\uD83D\uDE0A\">\uD83D\uDE0A</option>\r\n <option value=\"\uD83D\uDE00\">\uD83D\uDE00</option>\r\n <option value=\"\uD83D\uDE02\">\uD83D\uDE02</option>\r\n <option value=\"\uD83D\uDD25\">\uD83D\uDD25</option>\r\n <option value=\"\uD83C\uDF89\">\uD83C\uDF89</option>\r\n <option value=\"\uD83D\uDE80\">\uD83D\uDE80</option>\r\n <option value=\"\u2764\uFE0F\">\u2764\uFE0F</option>\r\n <option value=\"\uD83D\uDC4D\">\uD83D\uDC4D</option>\r\n </select>\r\n }\r\n </div>\r\n\r\n <div #element></div>\r\n</div>", styles: [".editor-shell{border:1px solid #dcdcdc;border-radius:10px;background:#fff;overflow:hidden;isolation:isolate;box-shadow:0 1px 2px #0000000a,0 6px 24px #0000000d;font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.editor-toolbar{display:flex;align-items:center;gap:4px;padding:4px;background:linear-gradient(to bottom,#fafafa,#f3f3f3);border-bottom:1px solid #e5e5e5;flex-wrap:wrap}.editor-toolbar button{display:inline-flex;align-items:center;justify-content:center;line-height:1;min-width:36px;height:36px;padding:0 12px;border:1px solid #ddd;border-radius:8px;background:transparent;color:#333;font-size:18px;font-weight:500;cursor:pointer;transition:background .12s ease,border-color .12s ease,color .12s ease}.editor-toolbar button:hover{background:#e9e9e9}.editor-toolbar button:active{transform:translateY(1px)}.editor-toolbar button.is-active{background:#dfe7ff;border-color:#c7d2fe;color:#2342c0}.editor-toolbar select{height:36px;padding:0 12px;border:1px solid #d6d6d6;border-radius:8px;background:#fff;font-size:14px;color:#222;cursor:pointer;transition:border-color .12s ease,box-shadow .12s ease,background .12s ease}.editor-toolbar select:hover{background:#fafafa}.editor-toolbar select:focus{outline:none;border-color:#8ba8ff;box-shadow:0 0 0 3px #6082ff26}.editor-toolbar .divider{width:1px;height:24px;background:#ddd;margin:0 2px}.ProseMirror{min-height:60px;padding:12px;outline:none;font-size:16px;line-height:1.7;color:#1f1f1f;caret-color:#2563eb}.ProseMirror h1,.ProseMirror h2,.ProseMirror h3{line-height:1.2;margin-top:.8em;margin-bottom:.4em;font-weight:700;letter-spacing:-.02em}.ProseMirror h1{font-size:2rem}.ProseMirror h2{font-size:1.5rem}.ProseMirror h3{font-size:1.25rem}.ProseMirror p{margin:0}.ProseMirror ul,.ProseMirror ol{padding-left:1.5rem;margin:5px 0}.ProseMirror li{margin:0}.ProseMirror blockquote{margin:1.4rem 0;padding-left:1rem;border-left:4px solid #c7d2fe;color:#555}.ProseMirror code{background:#f3f4f6;color:#d63384;padding:.15em .35em;border-radius:4px;font-size:.92em}.ProseMirror pre{background:#1f2937;color:#f9fafb;padding:16px 18px;border-radius:10px;overflow-x:auto;margin:1.2rem 0}.ProseMirror pre code{background:transparent;color:inherit;padding:0}.ProseMirror a{color:#2563eb;text-decoration:underline}.ProseMirror hr{border:none;border-top:1px solid #e2e2e2;margin:2rem 0}.ProseMirror table{width:100%;border-collapse:collapse;margin:1.2rem 0}.ProseMirror th,.ProseMirror td{border:1px solid #ddd;padding:5px;position:relative}.ProseMirror th{background:#f5f5f5;text-align:left}.ProseMirror .column-resize-handle{position:absolute;top:0;right:-2px;width:5px;height:100%;cursor:col-resize;background:transparent;z-index:20}.ProseMirror ::selection{background:#6082ff40}.suggestion-popup{display:flex;flex-direction:column;min-width:220px;padding:6px;background:#fff;border:1px solid #e5e5e5;border-radius:10px;box-shadow:0 12px 30px #00000014,0 2px 8px #0000000d;z-index:1000}.suggestion-popup button{border:none;background:transparent;border-radius:8px;padding:8px 10px;text-align:left;cursor:pointer;font-size:14px}.suggestion-popup button:hover,.suggestion-popup button.is-selected{background:#eef2ff}.mention{display:inline-flex;align-items:center;background:#eef2ff;color:#304ffe;padding:2px 6px;border-radius:5px;font-weight:500}.img-wrapper{position:relative;display:inline-block;margin:12px 0}.img-wrapper img{max-width:100%;border-radius:10px;transition:opacity .2s ease}.img-toolbar{position:absolute;top:8px;left:8px;display:flex;gap:6px;background:#fffffff2;border:1px solid #e5e5e5;border-radius:8px;padding:4px;box-shadow:0 8px 20px #00000014}.img-toolbar button{border:none;background:transparent;padding:4px 6px;cursor:pointer;font-size:12px}.img-toolbar button:hover{background:#eef2ff;border-radius:6px}.img-resize-handle{position:absolute;right:0;bottom:0;width:12px;height:12px;background:#2563eb;border-radius:3px;cursor:se-resize}\n"] }]
511
+ }], propDecorators: { inputChange: [{
512
+ type: Output
513
+ }], inputFocus: [{
514
+ type: Output
515
+ }], inputFinalized: [{
516
+ type: Output
517
+ }] } });
518
+
519
+ const TXE_DEBUG = new InjectionToken('TXE_DEBUG');
664
520
 
665
521
  /*
666
522
  * Public API Surface of text-editors
@@ -670,5 +526,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.15", ngImpo
670
526
  * Generated bundle index. Do not edit.
671
527
  */
672
528
 
673
- export { HtmlEditorComponent, HtmlEditorStandaloneComponent, MarkdownEditorComponent, MarkdownEditorStandaloneComponent, PersistentEditor, TXE_DEBUG };
529
+ export { EditorComponent, TXE_DEBUG };
674
530
  //# sourceMappingURL=esfaenza-text-editors.mjs.map