@arsedizioni/ars-utils 22.6.86 → 22.6.89

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.
Files changed (79) hide show
  1. package/fesm2022/arsedizioni-ars-utils-clipper.common.mjs +3158 -3344
  2. package/fesm2022/arsedizioni-ars-utils-clipper.common.mjs.map +1 -1
  3. package/fesm2022/arsedizioni-ars-utils-clipper.ui.mjs +7616 -4772
  4. package/fesm2022/arsedizioni-ars-utils-clipper.ui.mjs.map +1 -1
  5. package/fesm2022/arsedizioni-ars-utils-core.date.mjs +321 -702
  6. package/fesm2022/arsedizioni-ars-utils-core.date.mjs.map +1 -1
  7. package/fesm2022/arsedizioni-ars-utils-core.markdown.mjs +445 -590
  8. package/fesm2022/arsedizioni-ars-utils-core.markdown.mjs.map +1 -1
  9. package/fesm2022/arsedizioni-ars-utils-core.mjs +1735 -2370
  10. package/fesm2022/arsedizioni-ars-utils-core.mjs.map +1 -1
  11. package/fesm2022/arsedizioni-ars-utils-core.validators.mjs +1280 -1470
  12. package/fesm2022/arsedizioni-ars-utils-core.validators.mjs.map +1 -1
  13. package/fesm2022/arsedizioni-ars-utils-evolution.common.mjs +1097 -1528
  14. package/fesm2022/arsedizioni-ars-utils-evolution.common.mjs.map +1 -1
  15. package/fesm2022/arsedizioni-ars-utils-support.common.mjs +198 -294
  16. package/fesm2022/arsedizioni-ars-utils-support.common.mjs.map +1 -1
  17. package/fesm2022/arsedizioni-ars-utils-ui.controls.date.mjs +115 -117
  18. package/fesm2022/arsedizioni-ars-utils-ui.controls.date.mjs.map +1 -1
  19. package/fesm2022/arsedizioni-ars-utils-ui.controls.mjs +1607 -613
  20. package/fesm2022/arsedizioni-ars-utils-ui.controls.mjs.map +1 -1
  21. package/fesm2022/arsedizioni-ars-utils-ui.controls.tree.mjs +840 -367
  22. package/fesm2022/arsedizioni-ars-utils-ui.controls.tree.mjs.map +1 -1
  23. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.auth.mjs +1634 -651
  24. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.auth.mjs.map +1 -1
  25. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.mjs +1255 -792
  26. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.mjs.map +1 -1
  27. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.prompt.mjs +1410 -357
  28. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.prompt.mjs.map +1 -1
  29. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.select.mjs +2401 -944
  30. package/fesm2022/arsedizioni-ars-utils-ui.dialogs.select.mjs.map +1 -1
  31. package/fesm2022/arsedizioni-ars-utils-ui.files.mjs +1553 -741
  32. package/fesm2022/arsedizioni-ars-utils-ui.files.mjs.map +1 -1
  33. package/fesm2022/arsedizioni-ars-utils-ui.filters.mjs +901 -430
  34. package/fesm2022/arsedizioni-ars-utils-ui.filters.mjs.map +1 -1
  35. package/fesm2022/arsedizioni-ars-utils-ui.help.mjs +737 -530
  36. package/fesm2022/arsedizioni-ars-utils-ui.help.mjs.map +1 -1
  37. package/fesm2022/arsedizioni-ars-utils-ui.mjs +6638 -1755
  38. package/fesm2022/arsedizioni-ars-utils-ui.mjs.map +1 -1
  39. package/fesm2022/arsedizioni-ars-utils-ui.navigation.mjs +1479 -1207
  40. package/fesm2022/arsedizioni-ars-utils-ui.navigation.mjs.map +1 -1
  41. package/fesm2022/arsedizioni-ars-utils-ui.notifications.mjs +1003 -270
  42. package/fesm2022/arsedizioni-ars-utils-ui.notifications.mjs.map +1 -1
  43. package/fesm2022/arsedizioni-ars-utils-ui.oauth.mjs +497 -278
  44. package/fesm2022/arsedizioni-ars-utils-ui.oauth.mjs.map +1 -1
  45. package/fesm2022/arsedizioni-ars-utils-ui.paginator.mjs +48 -52
  46. package/fesm2022/arsedizioni-ars-utils-ui.paginator.mjs.map +1 -1
  47. package/fesm2022/arsedizioni-ars-utils-ui.shell.mjs +464 -704
  48. package/fesm2022/arsedizioni-ars-utils-ui.shell.mjs.map +1 -1
  49. package/fesm2022/arsedizioni-ars-utils-ui.tinymce.mjs +724 -657
  50. package/fesm2022/arsedizioni-ars-utils-ui.tinymce.mjs.map +1 -1
  51. package/fesm2022/arsedizioni-ars-utils.mjs +3 -19
  52. package/fesm2022/arsedizioni-ars-utils.mjs.map +1 -1
  53. package/package.json +1 -1
  54. package/types/arsedizioni-ars-utils-clipper.common.d.ts +1151 -1163
  55. package/types/arsedizioni-ars-utils-clipper.ui.d.ts +1588 -1426
  56. package/types/arsedizioni-ars-utils-core.d.ts +1169 -1172
  57. package/types/arsedizioni-ars-utils-core.date.d.ts +204 -208
  58. package/types/arsedizioni-ars-utils-core.markdown.d.ts +128 -126
  59. package/types/arsedizioni-ars-utils-core.validators.d.ts +270 -236
  60. package/types/arsedizioni-ars-utils-evolution.common.d.ts +1054 -1067
  61. package/types/arsedizioni-ars-utils-support.common.d.ts +225 -234
  62. package/types/arsedizioni-ars-utils-ui.controls.d.ts +440 -278
  63. package/types/arsedizioni-ars-utils-ui.controls.date.d.ts +24 -28
  64. package/types/arsedizioni-ars-utils-ui.controls.tree.d.ts +172 -136
  65. package/types/arsedizioni-ars-utils-ui.d.ts +2035 -821
  66. package/types/arsedizioni-ars-utils-ui.dialogs.auth.d.ts +327 -322
  67. package/types/arsedizioni-ars-utils-ui.dialogs.d.ts +343 -347
  68. package/types/arsedizioni-ars-utils-ui.dialogs.prompt.d.ts +167 -167
  69. package/types/arsedizioni-ars-utils-ui.dialogs.select.d.ts +373 -367
  70. package/types/arsedizioni-ars-utils-ui.files.d.ts +335 -283
  71. package/types/arsedizioni-ars-utils-ui.filters.d.ts +232 -160
  72. package/types/arsedizioni-ars-utils-ui.help.d.ts +204 -211
  73. package/types/arsedizioni-ars-utils-ui.navigation.d.ts +811 -646
  74. package/types/arsedizioni-ars-utils-ui.notifications.d.ts +74 -78
  75. package/types/arsedizioni-ars-utils-ui.oauth.d.ts +102 -62
  76. package/types/arsedizioni-ars-utils-ui.paginator.d.ts +19 -22
  77. package/types/arsedizioni-ars-utils-ui.shell.d.ts +376 -382
  78. package/types/arsedizioni-ars-utils-ui.tinymce.d.ts +244 -231
  79. package/types/arsedizioni-ars-utils.d.ts +1 -3
@@ -1,658 +1,725 @@
1
- import * as i0 from '@angular/core';
2
- import { Service, input, output, inject, ElementRef, ChangeDetectorRef, forwardRef, Directive, signal, ChangeDetectionStrategy, Component } from '@angular/core';
3
- import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
- import { ThemeService } from '@arsedizioni/ars-utils/core';
5
- import { form, disabled, FormField } from '@angular/forms/signals';
6
- import * as i2 from '@angular/material/button';
7
- import { MatButtonModule } from '@angular/material/button';
8
- import { MatDialogRef, MAT_DIALOG_DATA, MatDialogContent, MatDialogActions, MatDialogClose } from '@angular/material/dialog';
9
- import * as i1 from '@arsedizioni/ars-utils/ui';
10
- import { FlexLayoutModule } from '@arsedizioni/ars-utils/ui';
11
-
12
- /**
13
- * The editor profiles the applications ask for, ready to be handed to
14
- * `[tinymceConfig]` on {@link TinymceEditorDirective}.
15
- *
16
- * They describe **what the editor offers** — plugins, toolbar, formats, link and image
17
- * behaviour — and nothing else. Everything about *loading* TinyMCE (`base_url`, `suffix`,
18
- * `language`, `language_url`, `license_key`) and about the *theme* (`skin`, `content_css`)
19
- * belongs to `TinymceLoaderService` and to the directive, which resolve them against the
20
- * document base href and against `ThemeService`: a preset repeating them could only get them
21
- * wrong, and used to — the old ones pinned a relative `assets/tinymce` that broke under a
22
- * sub-path, and read the skin from `prefers-color-scheme` instead of the theme the user chose.
23
- *
24
- * Uploads are the same story: `automatic_uploads`, `paste_data_images` and `file_picker_types`
25
- * follow the `tinymceImageUploader` input, so an editor without a handler cannot end up
26
- * offering an upload tab that leads nowhere.
27
- *
28
- * Each accessor returns a **fresh deep copy**: a caller that tweaks a returned object — as the
29
- * editor dialog does with its toolbar — cannot corrupt the preset for everyone else.
30
- */
31
- class TinymceUtils {
32
- /**
33
- * Everything TinyMCE 8 open source has to offer, for a full-page editor.
34
- *
35
- * Adapted from the TinyMCE 5 profile the library used to ship: `paste`, `print`, `hr`,
36
- * `textpattern` and the `fullpage_*` options no longer exist (paste and text patterns moved
37
- * into the core, `fullpage` and `print` were dropped in v6), and `checklist`,
38
- * `openCodeMirrorButton` and `insertMediaButton` are premium or application-specific buttons
39
- * that a self-hosted GPL build has no way to render.
40
- */
41
- static { this.FULL = {
42
- height: 500,
43
- width: '100%',
44
- min_height: 250,
45
- onboarding: false,
46
- browser_spellcheck: true,
47
- custom_undo_redo_levels: 50,
48
- // Three   per TAB, and no wrapping of the inserted spaces.
49
- nonbreaking_force_tab: true,
50
- nonbreaking_wrap: false,
51
- help_tabs: ['shortcuts', 'keyboardnav', 'versions'],
52
- menubar: false,
53
- plugins: 'advlist anchor autolink autosave charmap code fullscreen help image insertdatetime'
54
- + ' link lists media nonbreaking pagebreak preview quickbars searchreplace table'
55
- + ' visualblocks visualchars',
56
- toolbar: [
57
- 'fullscreen | undo redo searchreplace | code preview | link image media charmap nonbreaking | table tableprops tabledelete',
58
- 'bold italic superscript subscript forecolor backcolor | alignleft aligncenter alignright alignjustify | removeformat | bullist numlist'
59
- ],
60
- quickbars_insert_toolbar: false,
61
- quickbars_selection_toolbar: 'removeformat | bold italic | superscript subscript | quicklink h2 h3 blockquote',
62
- style_formats: [
63
- {
64
- title: 'Immagini float', items: [
65
- { title: 'Immagine a SX', selector: 'img', styles: { float: 'left', margin: '0 10px 0 10px' } },
66
- { title: 'Immagine a DX', selector: 'img', styles: { float: 'right', margin: '0 10px 0 10px' } }
67
- ]
68
- },
69
- {
70
- title: 'Formati', items: [
71
- { title: 'Grassetto', format: 'bold' },
72
- { title: 'Corsivo', format: 'italic' },
73
- { title: 'Sottolineato', format: 'underline' },
74
- { title: 'Barrato', format: 'strikethrough' },
75
- { title: 'Superscript', format: 'superscript' },
76
- { title: 'Subscript', format: 'subscript' },
77
- { title: 'Codice', format: 'code' }
78
- ]
79
- }
80
- ],
81
- // Spans with an inline style rather than <u>/<strike>: the content travels by e-mail, where
82
- // the presentational tags are the first thing a client strips.
83
- formats: {
84
- underline: { inline: 'span', styles: { 'text-decoration': 'underline' }, exact: true },
85
- strikethrough: { inline: 'span', styles: { 'text-decoration': 'line-through' }, exact: true }
86
- },
87
- insertdatetime_formats: ['%d/%m/%Y', '%d %b %Y', '%A, %d %B %Y'],
88
- link_default_target: '_blank',
89
- link_title: true,
90
- link_assume_external_targets: 'https',
91
- link_class_list: [
92
- { title: 'Nessuno', value: '' },
93
- { title: 'Rilevante', value: 'relevant' },
94
- ],
95
- link_context_toolbar: true,
96
- link_quicklink: true,
97
- image_advtab: true,
98
- image_caption: true,
99
- image_title: true,
100
- }; }
101
- /**
102
- * The one for a field inside a form: it grows with the text instead of owning the page, the
103
- * toolbar sits under it and collapses into groups, and there is no status bar or context menu
104
- * to compete with the form around it.
105
- */
106
- static { this.COMPACT = {
107
- ...TinymceUtils.FULL,
108
- height: 200,
109
- min_height: 200,
110
- max_height: 500,
111
- statusbar: false,
112
- contextmenu: false,
113
- toolbar_location: 'bottom',
114
- plugins: 'advlist anchor autolink autoresize autosave charmap code image insertdatetime'
115
- + ' link lists media nonbreaking preview quickbars searchreplace table'
116
- + ' visualblocks visualchars',
117
- toolbar: ['undo redo | quickimage | formatgroup paragraphgroup'],
118
- toolbar_groups: {
119
- formatgroup: {
120
- icon: 'format',
121
- tooltip: 'Formattazione',
122
- items: 'bold italic underline superscript subscript | forecolor backcolor | removeformat'
123
- },
124
- paragraphgroup: {
125
- icon: 'paragraph',
126
- tooltip: 'Formato paragrafo',
127
- items: 'h1 h2 h3 | bullist numlist | alignleft aligncenter alignright alignjustify | indent outdent'
128
- },
129
- insertgroup: {
130
- icon: 'plus',
131
- tooltip: 'Inserisci',
132
- items: 'quickimage media | link charmap nonbreaking | table tableprops tabledelete'
133
- }
134
- },
135
- quickbars_insert_toolbar: 'quickimage',
136
- link_class_list: [],
137
- }; }
138
- /** The compact one plus full screen, the insert group and the source view. */
139
- static { this.COMPACT_EXTENDED = {
140
- ...TinymceUtils.COMPACT,
141
- plugins: 'advlist anchor autolink autoresize autosave charmap code fullscreen image'
142
- + ' insertdatetime link lists media nonbreaking preview quickbars searchreplace table'
143
- + ' visualblocks visualchars',
144
- toolbar: ['undo redo | quickimage insertgroup | formatgroup paragraphgroup | code | fullscreen'],
145
- }; }
146
- /**
147
- * The full-page profile.
148
- * @returns A fresh copy of the configuration, safe to modify.
149
- */
150
- static get TinymceConfig() {
151
- return structuredClone(TinymceUtils.FULL);
152
- }
153
- /**
154
- * The in-form profile.
155
- * @returns A fresh copy of the configuration, safe to modify.
156
- */
157
- static get TinymceCompactConfig() {
158
- return structuredClone(TinymceUtils.COMPACT);
159
- }
160
- /**
161
- * The in-form profile with full screen and source view.
162
- * @returns A fresh copy of the configuration, safe to modify.
163
- */
164
- static get TinymceCompactExtendedConfig() {
165
- return structuredClone(TinymceUtils.COMPACT_EXTENDED);
166
- }
167
- }
168
-
169
- /**
170
- * Loads the self-hosted TinyMCE bundle on demand.
171
- *
172
- * TinyMCE is deliberately NOT imported by any module: a static import would pull ~500 kB into
173
- * whichever chunk referenced it, and the editor is needed by exactly one dialog. The library is
174
- * copied to `assets/tinymce` by the build (see `angular.json`) and injected here as a plain
175
- * `<script>` the first time an editor is created, so it costs nothing until then and is served
176
- * from the browser cache afterwards. Loading it this way also lets TinyMCE resolve its own theme,
177
- * model, plugins and skin relative to `base_url`, which is how the self-hosted distribution
178
- * expects to work.
179
- */
180
- class TinymceLoaderService {
181
- /**
182
- * The absolute URL of the TinyMCE asset folder, resolved against the document base href so the
183
- * app keeps working when it is served from a sub-path.
184
- * @returns The base URL, without a trailing slash.
185
- */
186
- get baseUrl() {
187
- return new URL('assets/tinymce', document.baseURI).href.replace(/\/$/, '');
188
- }
189
- /**
190
- * Loads TinyMCE, reusing the script already injected by a previous call.
191
- * @returns A promise resolving with the global TinyMCE instance.
192
- */
193
- load() {
194
- if (window.tinymce)
195
- return Promise.resolve(window.tinymce);
196
- // A failed load clears `pending`, so a later attempt can retry instead of being stuck on the
197
- // rejected promise forever (e.g. the first open happened while the network was down).
198
- this.pending ??= new Promise((resolve, reject) => {
199
- const script = document.createElement('script');
200
- script.src = `${this.baseUrl}/tinymce.min.js`;
201
- script.async = true;
202
- script.referrerPolicy = 'origin';
203
- script.onload = () => {
204
- if (window.tinymce) {
205
- resolve(window.tinymce);
206
- }
207
- else {
208
- this.pending = undefined;
209
- reject(new Error('TinyMCE caricato ma non disponibile.'));
210
- }
211
- };
212
- script.onerror = () => {
213
- this.pending = undefined;
214
- script.remove();
215
- reject(new Error('Impossibile caricare l\'editor.'));
216
- };
217
- document.head.appendChild(script);
218
- });
219
- return this.pending;
220
- }
221
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TinymceLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Service }); }
222
- static { this.ɵprov = i0.ɵɵngDeclareService({ minVersion: "22.0.0", version: "22.1.7", ngImport: i0, type: TinymceLoaderService }); }
223
- }
224
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TinymceLoaderService, decorators: [{
225
- type: Service
226
- }] });
227
-
228
- /**
229
- * Turns a `<textarea>` into a rich text editor backed by the self-hosted TinyMCE bundle, and wires
230
- * it to Angular forms as a `ControlValueAccessor`, so `[(ngModel)]`, `required` and the
231
- * pristine/dirty state keep working exactly as they do on a plain control.
232
- *
233
- * The library itself is fetched on demand by <see cref="TinymceLoaderService"/>: nothing about
234
- * TinyMCE reaches the bundle until an editor is actually created.
235
- *
236
- * Usage: `<textarea tinymceEditor [(ngModel)]="item.text" name="text" required></textarea>`
237
- *
238
- */
239
- class TinymceEditorDirective {
240
- constructor() {
241
- /** Extra TinyMCE options, merged over (and able to override) the defaults below. */
242
- this.tinymceConfig = input({}, /* @ts-ignore */
243
- ...(ngDevMode ? [{ debugName: "tinymceConfig" }] : /* istanbul ignore next */ []));
244
- /** Whether the toolbar is pinned above the content (`fixed`) or floats on focus (`inline`). */
245
- this.tinymceToolbarMode = input('fixed', /* @ts-ignore */
246
- ...(ngDevMode ? [{ debugName: "tinymceToolbarMode" }] : /* istanbul ignore next */ []));
247
- /**
248
- * Handler invoked for every image dropped, pasted or picked in the editor. When not provided
249
- * the upload tab is hidden and only images referenced by URL can be inserted.
250
- */
251
- this.tinymceImageUploader = input(undefined, /* @ts-ignore */
252
- ...(ngDevMode ? [{ debugName: "tinymceImageUploader" }] : /* istanbul ignore next */ []));
253
- /** Emitted once the editor is up, for callers that want to enable UI only when it is usable. */
254
- this.ready = output();
255
- /** Emitted when the editor could not be loaded or initialised. */
256
- this.failed = output();
257
- this.host = inject(ElementRef);
258
- this.loader = inject(TinymceLoaderService);
259
- this.themeService = inject(ThemeService);
260
- this.changeDetector = inject(ChangeDetectorRef);
261
- /** True while the editor is showing itself full screen. */
262
- this.fullscreen = false;
263
- /** True once the directive has been destroyed, so a late init resolves into a no-op. */
264
- this.destroyed = false;
265
- /**
266
- * True while the directive itself is pushing a value into the editor. TinyMCE raises the same
267
- * content events for a programmatic `setContent` as for a keystroke, so without this flag
268
- * `writeValue` would immediately echo back through `onChange` and mark a freshly loaded form
269
- * as dirty.
270
- */
271
- this.writingValue = false;
272
- /** The value received before the editor existed, applied as soon as it does. */
273
- this.value = '';
274
- this.disabled = false;
275
- this.onChange = () => { };
276
- this.onTouched = () => { };
277
- /**
278
- * Leaves full screen on Escape, for the keystrokes that happen outside the content: with the
279
- * classic editor the content is an iframe, and a keydown in there never reaches this document.
280
- * Bound on the capture phase and stopped, so a dialog hosting the editor does not take the
281
- * Escape and close, throwing the edit away when the user only meant to leave full screen.
282
- */
283
- this.onDocumentKeydown = (event) => {
284
- if (!this.shouldEscapeLeaveFullscreen(event))
285
- return;
286
- event.preventDefault();
287
- event.stopPropagation();
288
- this.exitFullscreen();
289
- };
290
- }
291
- /**
292
- * Loads TinyMCE and initialises the editor over the host textarea.
293
- * @returns A promise that completes once the editor is ready (or has failed).
294
- */
295
- async ngOnInit() {
296
- try {
297
- const tinymce = await this.loader.load();
298
- if (this.destroyed)
299
- return;
300
- const dark = this.themeService.getTheme() === 'dark';
301
- const uploader = this.tinymceImageUploader();
302
- const inline = this.tinymceToolbarMode() === 'inline';
303
- // Caught here rather than left to TinyMCE, which would fail deep inside its own setup
304
- // with a message that says nothing about the actual mistake.
305
- if (inline && this.host.nativeElement.tagName === 'TEXTAREA') {
306
- throw new Error('La modalità inline richiede un <div>, non un <textarea>.');
307
- }
308
- // TinyMCE ships its language packs outside the npm package, so `it.js` is committed
309
- // under `src/assets/tinymce/langs/`: the existing `src/assets` build rule publishes it
310
- // exactly where TinyMCE looks for it, relative to `base_url`.
311
- const defaults = {
312
- // Self-hosting TinyMCE 8 requires an explicit license key: without one the editor
313
- // refuses to start. 'gpl' selects the GPLv2+ terms.
314
- license_key: 'gpl',
315
- language: 'it',
316
- branding: false,
317
- promotion: false,
318
- menubar: false,
319
- statusbar: false,
320
- resize: false,
321
- // Pins the toolbar while the container scrolls. In inline mode it is what keeps
322
- // the floating toolbar attached to the text instead of drifting off with it.
323
- toolbar_sticky: true,
324
- // An inline editor takes the size of the element it edits; only the classic one
325
- // has a box of its own to dimension.
326
- ...(inline ? {} : { height: 360 }),
327
- // The dialog body scrolls, and 'split' is what keeps the toolbar menus anchored
328
- // to the editor while it does: the popup sink is attached next to the editor, so
329
- // it shares its scrolling ancestry. It does NOT affect the modal dialogs — those
330
- // always go to <body> — which is why the CDK overlay has to be kept out of the
331
- // top layer (see OVERLAY_DEFAULT_CONFIG in app.config.ts).
332
- ui_mode: 'split',
333
- skin: dark ? 'oxide-dark' : 'oxide',
334
- content_css: dark ? 'dark' : 'default',
335
- // Absolute URLs must survive untouched: the content ends up in an e-mail, where a
336
- // relative image path resolves against nothing.
337
- convert_urls: false,
338
- plugins: 'advlist autolink lists link image table charmap searchreplace code fullscreen',
339
- toolbar: 'undo redo | blocks | bold italic underline | forecolor backcolor'
340
- + ' | bullist numlist | link image table | removeformat code fullscreen',
341
- content_style: 'body { font-family: Arial, Helvetica, sans-serif; font-size: 14px; }',
342
- // Pasted and dropped images go through the same upload path as the image dialog.
343
- automatic_uploads: !!uploader,
344
- paste_data_images: !!uploader,
345
- images_file_types: 'jpeg,jpg,png,gif,webp',
346
- file_picker_types: uploader ? 'image' : '',
347
- images_upload_handler: uploader
348
- ? (blobInfo) => uploader(new File([blobInfo.blob()], blobInfo.filename(), { type: blobInfo.blob().type }))
349
- : undefined,
350
- };
351
- await tinymce.init({
352
- ...defaults,
353
- // The caller's profile wins over every default above: that is what the presets in
354
- // `TinymceUtils` are for.
355
- ...this.tinymceConfig(),
356
- // Not negotiable, whatever the profile says. `target` and `base_url` are resolved
357
- // here and nowhere else, `inline` is the input's job, and replacing `setup` would
358
- // unhook the editor from the form and leave a ControlValueAccessor that never
359
- // reports a change.
360
- target: this.host.nativeElement,
361
- base_url: this.loader.baseUrl,
362
- inline,
363
- setup: (editor) => {
364
- /*
365
- * Typing, toolbar commands, undo/redo and programmatic inserts all have to reach
366
- * the form; the two guards keep everything else out.
367
- *
368
- * `writingValue` keeps our own writes from bouncing back. `!this.editor` — which
369
- * stays unset until the `init` handler below — keeps out the one content event
370
- * that fires BEFORE the editor exists, and it is the one that made an editor
371
- * impossible to fill: TinyMCE's own `loadInitialContent` reads the textarea and
372
- * calls `setContent(..., { initial: true })`, which dispatches `SetContent`
373
- * because only `no_events` suppresses it. The textarea is empty — the form
374
- * writes through the ControlValueAccessor, never into the DOM element — so this
375
- * handler took that empty string for a user edit, overwrote the value the form
376
- * had already handed us, and reported '' back to the model. `init` then applied
377
- * the value it had just destroyed, and the editor came up blank whatever was
378
- * passed to it. Saving from there stored the empty string as well.
379
- */
380
- editor.on('change input undo redo SetContent', () => {
381
- if (this.writingValue || !this.editor)
382
- return;
383
- this.value = editor.getContent();
384
- this.onChange(this.value);
385
- this.changeDetector.markForCheck();
386
- });
387
- editor.on('blur', () => {
388
- this.onTouched();
389
- this.changeDetector.markForCheck();
390
- });
391
- editor.on('FullscreenStateChanged', (event) => {
392
- this.handleFullscreenChanged(event.state === true);
393
- });
394
- // The content of a classic editor lives in an iframe, so its keystrokes are
395
- // only visible from the editor itself.
396
- editor.on('keydown', (event) => {
397
- if (!this.shouldEscapeLeaveFullscreen(event))
398
- return;
399
- event.preventDefault();
400
- event.stopPropagation();
401
- this.exitFullscreen();
402
- });
403
- editor.on('init', () => {
404
- // Assigned BEFORE applying the value, and that order is the contract with
405
- // the guard above: from here on content events are genuine, and the one
406
- // raised by `applyValue` itself is covered by `writingValue`.
407
- this.editor = editor;
408
- this.applyValue(this.value);
409
- this.applyDisabled(this.disabled);
410
- this.ready.emit(editor);
411
- this.changeDetector.markForCheck();
412
- });
413
- },
414
- });
415
- }
416
- catch (error) {
417
- if (this.destroyed)
418
- return;
419
- this.failed.emit(error instanceof Error ? error : new Error(String(error)));
420
- this.changeDetector.markForCheck();
421
- }
422
- }
423
- /**
424
- * Tears the editor down. TinyMCE keeps global state per instance, so leaving it behind when a
425
- * dialog closes leaks both DOM and editor registrations.
426
- * @returns void
427
- */
428
- ngOnDestroy() {
429
- this.destroyed = true;
430
- // Removing the editor does not raise the full-screen event, so the session is closed by
431
- // hand here: the Escape listener would otherwise stay attached to the document forever.
432
- this.releaseFullscreen();
433
- this.editor?.remove();
434
- this.editor = undefined;
435
- }
436
- /**
437
- * Decides whether an Escape press means "leave full screen": it does whenever the editor is
438
- * full screen, full stop. While full screen the editor owns Escape, and the press is stopped
439
- * here so it can never reach the dialog hosting it — leaving full screen must not also throw
440
- * away what is being written.
441
- * @param event The keyboard event, from the document or from the editor's content.
442
- * @returns True when full screen should be left.
443
- */
444
- shouldEscapeLeaveFullscreen(event) {
445
- return event.key === 'Escape' && this.fullscreen;
446
- }
447
- /**
448
- * Asks the editor to leave full screen. Safe to call when it is not in full screen.
449
- * @returns void
450
- */
451
- exitFullscreen() {
452
- if (!this.fullscreen)
453
- return;
454
- this.editor?.execCommand('mceFullScreen');
455
- }
456
- /**
457
- * Attaches or detaches everything that only makes sense while the editor is full screen.
458
- * @param state True when the editor has just entered full screen.
459
- * @returns void
460
- */
461
- handleFullscreenChanged(state) {
462
- if (state === this.fullscreen)
463
- return;
464
- this.fullscreen = state;
465
- if (state) {
466
- document.addEventListener('keydown', this.onDocumentKeydown, true);
467
- }
468
- else {
469
- this.releaseFullscreen();
470
- }
471
- this.changeDetector.markForCheck();
472
- }
473
- /**
474
- * Detaches the listener that only makes sense while the editor is full screen.
475
- * @returns void
476
- */
477
- releaseFullscreen() {
478
- document.removeEventListener('keydown', this.onDocumentKeydown, true);
479
- this.fullscreen = false;
480
- }
481
- /**
482
- * Pushes a value coming from the form into the editor.
483
- * @param value The new content, or null/undefined for an empty editor.
484
- * @returns void
485
- */
486
- writeValue(value) {
487
- this.value = value ?? '';
488
- this.applyValue(this.value);
489
- }
490
- /**
491
- * Registers the callback used to report content changes to the form.
492
- * @param fn The callback supplied by Angular forms.
493
- * @returns void
494
- */
495
- registerOnChange(fn) {
496
- this.onChange = fn;
497
- }
498
- /**
499
- * Registers the callback used to report the first blur to the form.
500
- * @param fn The callback supplied by Angular forms.
501
- * @returns void
502
- */
503
- registerOnTouched(fn) {
504
- this.onTouched = fn;
505
- }
506
- /**
507
- * Enables or disables editing.
508
- * @param isDisabled True to switch the editor to read-only.
509
- * @returns void
510
- */
511
- setDisabledState(isDisabled) {
512
- this.disabled = isDisabled;
513
- this.applyDisabled(isDisabled);
514
- }
515
- /**
516
- * Writes content into the editor when it exists, guarding against the echo described on
517
- * <see cref="writingValue"/>.
518
- * @param value The content to write.
519
- * @returns void
520
- */
521
- applyValue(value) {
522
- const editor = this.editor;
523
- if (!editor)
524
- return;
525
- // Comparing with the serialized content avoids resetting the caret on every form patch
526
- // that did not actually change anything.
527
- if (editor.getContent() === value)
528
- return;
529
- this.writingValue = true;
530
- try {
531
- editor.setContent(value);
532
- }
533
- finally {
534
- this.writingValue = false;
535
- }
536
- }
537
- /**
538
- * Applies the disabled state to the editor when it exists.
539
- * @param isDisabled True to switch the editor to read-only.
540
- * @returns void
541
- */
542
- applyDisabled(isDisabled) {
543
- this.editor?.mode.set(isDisabled ? 'readonly' : 'design');
544
- }
545
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TinymceEditorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
546
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.7", type: TinymceEditorDirective, isStandalone: true, selector: "textarea[tinymceEditor], div[tinymceEditor]", inputs: { tinymceConfig: { classPropertyName: "tinymceConfig", publicName: "tinymceConfig", isSignal: true, isRequired: false, transformFunction: null }, tinymceToolbarMode: { classPropertyName: "tinymceToolbarMode", publicName: "tinymceToolbarMode", isSignal: true, isRequired: false, transformFunction: null }, tinymceImageUploader: { classPropertyName: "tinymceImageUploader", publicName: "tinymceImageUploader", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { ready: "ready", failed: "failed" }, providers: [{
547
- provide: NG_VALUE_ACCESSOR,
548
- useExisting: forwardRef(() => TinymceEditorDirective),
549
- multi: true
550
- }], ngImport: i0 }); }
551
- }
552
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TinymceEditorDirective, decorators: [{
553
- type: Directive,
554
- args: [{
555
- // Both hosts are accepted because the two modes need different ones: the classic editor
556
- // replaces a textarea, the inline editor edits a div in place.
557
- selector: 'textarea[tinymceEditor], div[tinymceEditor]',
558
- providers: [{
559
- provide: NG_VALUE_ACCESSOR,
560
- useExisting: forwardRef(() => TinymceEditorDirective),
561
- multi: true
562
- }]
563
- }]
564
- }], propDecorators: { tinymceConfig: [{ type: i0.Input, args: [{ isSignal: true, alias: "tinymceConfig", required: false }] }], tinymceToolbarMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "tinymceToolbarMode", required: false }] }], tinymceImageUploader: [{ type: i0.Input, args: [{ isSignal: true, alias: "tinymceImageUploader", required: false }] }], ready: [{ type: i0.Output, args: ["ready"] }], failed: [{ type: i0.Output, args: ["failed"] }] } });
565
-
566
- /**
567
- * A dialog that is nothing but an editor: the caller hands it a text and gets the edited one
568
- * back through {@link saving}.
569
- *
570
- * The editor is {@link TinymceEditorDirective} on a plain `<textarea>`, so this dialog costs
571
- * exactly what any other editor in the application costs — the TinyMCE bundle is fetched on
572
- * first use and nothing about it reaches the initial chunk.
573
- */
574
- class TinyMceEditorComponent {
575
- constructor() {
576
- /** Emitted with the edited text when the user saves. */
577
- this.saving = output();
578
- this.dialogRef = inject((MatDialogRef));
579
- /** Dialog configuration, injected and exposed as a signal. */
580
- this.dialogData = signal((() => {
581
- const data = inject(MAT_DIALOG_DATA) ?? {};
582
- return { ...data, text: data.text ?? '' };
583
- })(), /* @ts-ignore */
584
- ...(ngDevMode ? [{ debugName: "dialogData" }] : /* istanbul ignore next */ []));
585
- /** Whether the editor is in read-only mode. */
586
- this.disabled = signal(false, /* @ts-ignore */
587
- ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
588
- /**
589
- * Why the editor could not be shown, when it could not.
590
- *
591
- * Without it a failure looked exactly like an empty document: the directive reported it on its
592
- * `failed` output, nothing was listening, and the user was left with a blank box and no reason
593
- * for it. The message is the directive's own — the bundle did not load, or TinyMCE refused to
594
- * start — and it also goes to the console, because that is where whoever is asked about it will
595
- * look first.
596
- */
597
- this.error = signal(undefined, /* @ts-ignore */
598
- ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
599
- /** Current editor content. */
600
- this.text = signal('', /* @ts-ignore */
601
- ...(ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []));
602
- /**
603
- * Signal form bound to the editor textarea. The disabled state is pushed to the
604
- * TinymceEditorDirective through the ControlValueAccessor interop.
605
- */
606
- this.textForm = form(this.text, p => {
607
- disabled(p, () => this.disabled());
608
- });
609
- /** The profile handed to the editor directive, merged over its defaults. */
610
- this.tinymceConfig = {};
611
- const data = this.dialogData();
612
- this.text.set(data.text);
613
- // The accessor already returns a private copy, so the toolbar override below cannot leak
614
- // into the preset shared with the rest of the application.
615
- this.tinymceConfig = data.configuration
616
- ? { ...data.configuration }
617
- : TinymceUtils.TinymceCompactExtendedConfig;
618
- // No full screen: the editor already owns the whole dialog.
619
- this.tinymceConfig.toolbar = ['undo redo | quickimage insertgroup | formatgroup paragraphgroup | code'];
620
- this.disabled.set(data.disabled ?? false);
621
- }
622
- /**
623
- * Records that the editor could not be shown.
624
- * @param error - What the directive could not do.
625
- * @returns void
626
- */
627
- failed(error) {
628
- console.error('[TinyMceEditorComponent] editor non disponibile', error);
629
- this.error.set(error?.message || 'Editor non disponibile.');
630
- }
631
- /**
632
- * Save the current editor content and close the dialog.
633
- */
634
- ok() {
635
- this.saving.emit(this.text() ?? '');
636
- setTimeout(() => {
637
- this.dialogRef.close();
638
- }, 500);
639
- }
640
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TinyMceEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
641
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.7", type: TinyMceEditorComponent, isStandalone: true, selector: "ng-component", outputs: { saving: "saving" }, ngImport: i0, template: "<mat-dialog-content style=\"padding: 10px 10px 0 10px\">\n @if (error()) {\n <!-- The textarea is gone with the editor that could not replace it: left in place it would be a\n plain box the form does not write into, which is the empty editor this message exists to\n explain. -->\n <div class=\"editor-error\">\n <div class=\"editor-error-title\">Non \u00E8 stato possibile aprire l'editor.</div>\n <div class=\"editor-error-detail\">{{ error() }}</div>\n <div class=\"editor-error-detail\">Il testo non \u00E8 stato modificato: chiudi e riprova.</div>\n </div>\n } @else {\n <textarea tinymceEditor class=\"full-screen-editor\" [tinymceConfig]=\"tinymceConfig\"\n [formField]=\"textForm\" (failed)=\"failed($event)\"></textarea>\n }\n</mat-dialog-content>\n<mat-dialog-actions>\n <div fxLayout=\"row\" fxLayoutGap=\"10px\" fxLayoutAlign=\"start center\" fxFill>\n <div fxFlex=\"50\">\n @if(dialogData().onShowInfo) {\n <button mat-stroked-button (click)=\"dialogData().onShowInfo()\">{{dialogData().infoButtonLabel ?? 'Informazioni'}}\n </button>\n }\n </div>\n <div fxFlex=\"50\" fxLayoutAlign=\"end\">\n <!-- Nothing to save when there was no editor: the only way out is the one that changes\n nothing. -->\n @if (disabled() || error()) {\n <button mat-stroked-button [mat-dialog-close]=\"true\">Chiudi</button>\n } @else {\n <button mat-flat-button (click)=\"ok()\">Salva</button>\n <button mat-stroked-button [mat-dialog-close]=\"true\">Annulla</button>\n }\n </div>\n </div>\n</mat-dialog-actions>\n", styles: [".mat-mdc-dialog-content{height:calc(100% - 52px)!important;max-height:calc(100% - 52px)!important}::ng-deep .full-screen-editor+.tox-tinymce{height:100%!important}.editor-error{display:flex;flex-direction:column;gap:6px;justify-content:center;align-items:center;height:100%;min-height:120px;padding:24px;text-align:center}.editor-error .editor-error-title{font-weight:500}.editor-error .editor-error-detail{font-size:small;opacity:.8}\n"], dependencies: [{ kind: "directive", type: MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: TinymceEditorDirective, selector: "textarea[tinymceEditor], div[tinymceEditor]", inputs: ["tinymceConfig", "tinymceToolbarMode", "tinymceImageUploader"], outputs: ["ready", "failed"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "ngmodule", type: FlexLayoutModule }, { kind: "directive", type: i1.FxLayoutDirective, selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg] ", inputs: ["fxLayout", "fxLayout.xs", "fxLayout.sm", "fxLayout.md", "fxLayout.lg", "fxLayout.xl", "fxLayout.lt-sm", "fxLayout.lt-md", "fxLayout.lt-lg", "fxLayout.lt-xl", "fxLayout.gt-xs", "fxLayout.gt-sm", "fxLayout.gt-md", "fxLayout.gt-lg"] }, { kind: "directive", type: i1.FxFlexDirective, selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg] ", inputs: ["fxFlex", "fxFlex.xs", "fxFlex.sm", "fxFlex.md", "fxFlex.lg", "fxFlex.xl", "fxFlex.lt-sm", "fxFlex.lt-md", "fxFlex.lt-lg", "fxFlex.lt-xl", "fxFlex.gt-xs", "fxFlex.gt-sm", "fxFlex.gt-md", "fxFlex.gt-lg"] }, { kind: "directive", type: i1.FxLayoutAlignDirective, selector: " [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg] ", inputs: ["fxLayoutAlign", "fxLayoutAlign.xs", "fxLayoutAlign.sm", "fxLayoutAlign.md", "fxLayoutAlign.lg", "fxLayoutAlign.xl", "fxLayoutAlign.lt-sm", "fxLayoutAlign.lt-md", "fxLayoutAlign.lt-lg", "fxLayoutAlign.lt-xl", "fxLayoutAlign.gt-xs", "fxLayoutAlign.gt-sm", "fxLayoutAlign.gt-md", "fxLayoutAlign.gt-lg"] }, { kind: "directive", type: i1.FxLayoutGapDirective, selector: " [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg] ", inputs: ["fxLayoutGap", "fxLayoutGap.xs", "fxLayoutGap.sm", "fxLayoutGap.md", "fxLayoutGap.lg", "fxLayoutGap.xl", "fxLayoutGap.lt-sm", "fxLayoutGap.lt-md", "fxLayoutGap.lt-lg", "fxLayoutGap.lt-xl", "fxLayoutGap.gt-xs", "fxLayoutGap.gt-sm", "fxLayoutGap.gt-md", "fxLayoutGap.gt-lg"] }, { kind: "directive", type: i1.FxFlexFillDirective, selector: " [fxFlexFill], [fxFill], [fxFlexFill.xs], [fxFlexFill.sm], [fxFlexFill.md], [fxFlexFill.lg], [fxFlexFill.xl], [fxFlexFill.lt-sm], [fxFlexFill.lt-md], [fxFlexFill.lt-lg], [fxFlexFill.lt-xl], [fxFlexFill.gt-xs], [fxFlexFill.gt-sm], [fxFlexFill.gt-md], [fxFlexFill.gt-lg] ", inputs: ["fxFlexFill", "fxFill", "fxFlexFill.xs", "fxFlexFill.sm", "fxFlexFill.md", "fxFlexFill.lg", "fxFlexFill.xl", "fxFlexFill.lt-sm", "fxFlexFill.lt-md", "fxFlexFill.lt-lg", "fxFlexFill.lt-xl", "fxFlexFill.gt-xs", "fxFlexFill.gt-sm", "fxFlexFill.gt-md", "fxFlexFill.gt-lg"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i2.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
642
- }
643
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.7", ngImport: i0, type: TinyMceEditorComponent, decorators: [{
644
- type: Component,
645
- args: [{ standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [MatDialogContent, TinymceEditorDirective, FormField, MatDialogActions, FlexLayoutModule,
646
- MatButtonModule, MatDialogClose], template: "<mat-dialog-content style=\"padding: 10px 10px 0 10px\">\n @if (error()) {\n <!-- The textarea is gone with the editor that could not replace it: left in place it would be a\n plain box the form does not write into, which is the empty editor this message exists to\n explain. -->\n <div class=\"editor-error\">\n <div class=\"editor-error-title\">Non \u00E8 stato possibile aprire l'editor.</div>\n <div class=\"editor-error-detail\">{{ error() }}</div>\n <div class=\"editor-error-detail\">Il testo non \u00E8 stato modificato: chiudi e riprova.</div>\n </div>\n } @else {\n <textarea tinymceEditor class=\"full-screen-editor\" [tinymceConfig]=\"tinymceConfig\"\n [formField]=\"textForm\" (failed)=\"failed($event)\"></textarea>\n }\n</mat-dialog-content>\n<mat-dialog-actions>\n <div fxLayout=\"row\" fxLayoutGap=\"10px\" fxLayoutAlign=\"start center\" fxFill>\n <div fxFlex=\"50\">\n @if(dialogData().onShowInfo) {\n <button mat-stroked-button (click)=\"dialogData().onShowInfo()\">{{dialogData().infoButtonLabel ?? 'Informazioni'}}\n </button>\n }\n </div>\n <div fxFlex=\"50\" fxLayoutAlign=\"end\">\n <!-- Nothing to save when there was no editor: the only way out is the one that changes\n nothing. -->\n @if (disabled() || error()) {\n <button mat-stroked-button [mat-dialog-close]=\"true\">Chiudi</button>\n } @else {\n <button mat-flat-button (click)=\"ok()\">Salva</button>\n <button mat-stroked-button [mat-dialog-close]=\"true\">Annulla</button>\n }\n </div>\n </div>\n</mat-dialog-actions>\n", styles: [".mat-mdc-dialog-content{height:calc(100% - 52px)!important;max-height:calc(100% - 52px)!important}::ng-deep .full-screen-editor+.tox-tinymce{height:100%!important}.editor-error{display:flex;flex-direction:column;gap:6px;justify-content:center;align-items:center;height:100%;min-height:120px;padding:24px;text-align:center}.editor-error .editor-error-title{font-weight:500}.editor-error .editor-error-detail{font-size:small;opacity:.8}\n"] }]
647
- }], ctorParameters: () => [], propDecorators: { saving: [{ type: i0.Output, args: ["saving"] }] } });
648
-
649
- /*
650
- * Public API Surface of @arsedizioni/ars-utils/ui.tinymce
651
- */
652
-
653
- /**
654
- * Generated bundle index. Do not edit.
655
- */
656
-
1
+ import * as i0 from "@angular/core";
2
+ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Directive, ElementRef, Service, forwardRef, inject, input, output, signal } from "@angular/core";
3
+ import { NG_VALUE_ACCESSOR } from "@angular/forms";
4
+ import { ThemeService } from "@arsedizioni/ars-utils/core";
5
+ import { FormField, disabled, form } from "@angular/forms/signals";
6
+ import * as i2 from "@angular/material/button";
7
+ import { MatButtonModule } from "@angular/material/button";
8
+ import { MAT_DIALOG_DATA, MatDialogActions, MatDialogClose, MatDialogContent, MatDialogRef } from "@angular/material/dialog";
9
+ import * as i1 from "@arsedizioni/ars-utils/ui";
10
+ import { FlexLayoutModule } from "@arsedizioni/ars-utils/ui";
11
+ var TinymceUtils = class TinymceUtils {
12
+ static {
13
+ this.FULL = {
14
+ height: 500,
15
+ width: "100%",
16
+ min_height: 250,
17
+ onboarding: false,
18
+ browser_spellcheck: true,
19
+ custom_undo_redo_levels: 50,
20
+ nonbreaking_force_tab: true,
21
+ nonbreaking_wrap: false,
22
+ help_tabs: [
23
+ "shortcuts",
24
+ "keyboardnav",
25
+ "versions"
26
+ ],
27
+ menubar: false,
28
+ plugins: "advlist anchor autolink autosave charmap code fullscreen help image insertdatetime link lists media nonbreaking pagebreak preview quickbars searchreplace table visualblocks visualchars",
29
+ toolbar: ["fullscreen | undo redo searchreplace | code preview | link image media charmap nonbreaking | table tableprops tabledelete", "bold italic superscript subscript forecolor backcolor | alignleft aligncenter alignright alignjustify | removeformat | bullist numlist"],
30
+ quickbars_insert_toolbar: false,
31
+ quickbars_selection_toolbar: "removeformat | bold italic | superscript subscript | quicklink h2 h3 blockquote",
32
+ style_formats: [{
33
+ title: "Immagini float",
34
+ items: [{
35
+ title: "Immagine a SX",
36
+ selector: "img",
37
+ styles: {
38
+ float: "left",
39
+ margin: "0 10px 0 10px"
40
+ }
41
+ }, {
42
+ title: "Immagine a DX",
43
+ selector: "img",
44
+ styles: {
45
+ float: "right",
46
+ margin: "0 10px 0 10px"
47
+ }
48
+ }]
49
+ }, {
50
+ title: "Formati",
51
+ items: [
52
+ {
53
+ title: "Grassetto",
54
+ format: "bold"
55
+ },
56
+ {
57
+ title: "Corsivo",
58
+ format: "italic"
59
+ },
60
+ {
61
+ title: "Sottolineato",
62
+ format: "underline"
63
+ },
64
+ {
65
+ title: "Barrato",
66
+ format: "strikethrough"
67
+ },
68
+ {
69
+ title: "Superscript",
70
+ format: "superscript"
71
+ },
72
+ {
73
+ title: "Subscript",
74
+ format: "subscript"
75
+ },
76
+ {
77
+ title: "Codice",
78
+ format: "code"
79
+ }
80
+ ]
81
+ }],
82
+ formats: {
83
+ underline: {
84
+ inline: "span",
85
+ styles: { "text-decoration": "underline" },
86
+ exact: true
87
+ },
88
+ strikethrough: {
89
+ inline: "span",
90
+ styles: { "text-decoration": "line-through" },
91
+ exact: true
92
+ }
93
+ },
94
+ insertdatetime_formats: [
95
+ "%d/%m/%Y",
96
+ "%d %b %Y",
97
+ "%A, %d %B %Y"
98
+ ],
99
+ link_default_target: "_blank",
100
+ link_title: true,
101
+ link_assume_external_targets: "https",
102
+ link_class_list: [{
103
+ title: "Nessuno",
104
+ value: ""
105
+ }, {
106
+ title: "Rilevante",
107
+ value: "relevant"
108
+ }],
109
+ link_context_toolbar: true,
110
+ link_quicklink: true,
111
+ image_advtab: true,
112
+ image_caption: true,
113
+ image_title: true
114
+ };
115
+ }
116
+ static {
117
+ this.COMPACT = {
118
+ ...TinymceUtils.FULL,
119
+ height: 200,
120
+ min_height: 200,
121
+ max_height: 500,
122
+ statusbar: false,
123
+ contextmenu: false,
124
+ toolbar_location: "bottom",
125
+ plugins: "advlist anchor autolink autoresize autosave charmap code image insertdatetime link lists media nonbreaking preview quickbars searchreplace table visualblocks visualchars",
126
+ toolbar: ["undo redo | quickimage | formatgroup paragraphgroup"],
127
+ toolbar_groups: {
128
+ formatgroup: {
129
+ icon: "format",
130
+ tooltip: "Formattazione",
131
+ items: "bold italic underline superscript subscript | forecolor backcolor | removeformat"
132
+ },
133
+ paragraphgroup: {
134
+ icon: "paragraph",
135
+ tooltip: "Formato paragrafo",
136
+ items: "h1 h2 h3 | bullist numlist | alignleft aligncenter alignright alignjustify | indent outdent"
137
+ },
138
+ insertgroup: {
139
+ icon: "plus",
140
+ tooltip: "Inserisci",
141
+ items: "quickimage media | link charmap nonbreaking | table tableprops tabledelete"
142
+ }
143
+ },
144
+ quickbars_insert_toolbar: "quickimage",
145
+ link_class_list: []
146
+ };
147
+ }
148
+ static {
149
+ this.COMPACT_EXTENDED = {
150
+ ...TinymceUtils.COMPACT,
151
+ plugins: "advlist anchor autolink autoresize autosave charmap code fullscreen image insertdatetime link lists media nonbreaking preview quickbars searchreplace table visualblocks visualchars",
152
+ toolbar: ["undo redo | quickimage insertgroup | formatgroup paragraphgroup | code | fullscreen"]
153
+ };
154
+ }
155
+ static get TinymceConfig() {
156
+ return structuredClone(TinymceUtils.FULL);
157
+ }
158
+ static get TinymceCompactConfig() {
159
+ return structuredClone(TinymceUtils.COMPACT);
160
+ }
161
+ static get TinymceCompactExtendedConfig() {
162
+ return structuredClone(TinymceUtils.COMPACT_EXTENDED);
163
+ }
164
+ };
165
+ var TinymceLoaderService = class TinymceLoaderService {
166
+ get baseUrl() {
167
+ return new URL("assets/tinymce", document.baseURI).href.replace(/\/$/, "");
168
+ }
169
+ load() {
170
+ if (window.tinymce) return Promise.resolve(window.tinymce);
171
+ this.pending ??= new Promise((resolve, reject) => {
172
+ const script = document.createElement("script");
173
+ script.src = `${this.baseUrl}/tinymce.min.js`;
174
+ script.async = true;
175
+ script.referrerPolicy = "origin";
176
+ script.onload = () => {
177
+ if (window.tinymce) resolve(window.tinymce);
178
+ else {
179
+ this.pending = void 0;
180
+ reject(/* @__PURE__ */ new Error("TinyMCE caricato ma non disponibile."));
181
+ }
182
+ };
183
+ script.onerror = () => {
184
+ this.pending = void 0;
185
+ script.remove();
186
+ reject(/* @__PURE__ */ new Error("Impossibile caricare l'editor."));
187
+ };
188
+ document.head.appendChild(script);
189
+ });
190
+ return this.pending;
191
+ }
192
+ static {
193
+ this.ɵfac = i0.ɵɵngDeclareFactory({
194
+ minVersion: "12.0.0",
195
+ version: "22.2.1",
196
+ ngImport: i0,
197
+ type: TinymceLoaderService,
198
+ deps: [],
199
+ target: i0.ɵɵFactoryTarget.Service
200
+ });
201
+ }
202
+ static {
203
+ this.ɵprov = i0.ɵɵngDeclareService({
204
+ minVersion: "22.0.0",
205
+ version: "22.2.1",
206
+ ngImport: i0,
207
+ type: TinymceLoaderService
208
+ });
209
+ }
210
+ };
211
+ i0.ɵɵngDeclareClassMetadata({
212
+ minVersion: "12.0.0",
213
+ version: "22.2.1",
214
+ ngImport: i0,
215
+ type: TinymceLoaderService,
216
+ decorators: [{ type: Service }]
217
+ });
218
+ var TinymceEditorDirective = class TinymceEditorDirective {
219
+ constructor() {
220
+ this.tinymceConfig = input({}, ...ngDevMode ? [{ debugName: "tinymceConfig" }] : /* istanbul ignore next */ []);
221
+ this.tinymceToolbarMode = input("fixed", ...ngDevMode ? [{ debugName: "tinymceToolbarMode" }] : /* istanbul ignore next */ []);
222
+ this.tinymceImageUploader = input(void 0, ...ngDevMode ? [{ debugName: "tinymceImageUploader" }] : /* istanbul ignore next */ []);
223
+ this.ready = output();
224
+ this.failed = output();
225
+ this.host = inject(ElementRef);
226
+ this.loader = inject(TinymceLoaderService);
227
+ this.themeService = inject(ThemeService);
228
+ this.changeDetector = inject(ChangeDetectorRef);
229
+ this.fullscreen = false;
230
+ this.destroyed = false;
231
+ this.writingValue = false;
232
+ this.value = "";
233
+ this.disabled = false;
234
+ this.onChange = () => {};
235
+ this.onTouched = () => {};
236
+ this.onDocumentKeydown = (event) => {
237
+ if (!this.shouldEscapeLeaveFullscreen(event)) return;
238
+ event.preventDefault();
239
+ event.stopPropagation();
240
+ this.exitFullscreen();
241
+ };
242
+ }
243
+ async ngOnInit() {
244
+ try {
245
+ const tinymce = await this.loader.load();
246
+ if (this.destroyed) return;
247
+ const dark = this.themeService.getTheme() === "dark";
248
+ const uploader = this.tinymceImageUploader();
249
+ const inline = this.tinymceToolbarMode() === "inline";
250
+ if (inline && this.host.nativeElement.tagName === "TEXTAREA") throw new Error("La modalità inline richiede un <div>, non un <textarea>.");
251
+ const defaults = {
252
+ license_key: "gpl",
253
+ language: "it",
254
+ branding: false,
255
+ promotion: false,
256
+ menubar: false,
257
+ statusbar: false,
258
+ resize: false,
259
+ toolbar_sticky: true,
260
+ ...inline ? {} : { height: 360 },
261
+ ui_mode: "split",
262
+ skin: dark ? "oxide-dark" : "oxide",
263
+ content_css: dark ? "dark" : "default",
264
+ convert_urls: false,
265
+ plugins: "advlist autolink lists link image table charmap searchreplace code fullscreen",
266
+ toolbar: "undo redo | blocks | bold italic underline | forecolor backcolor | bullist numlist | link image table | removeformat code fullscreen",
267
+ content_style: "body { font-family: Arial, Helvetica, sans-serif; font-size: 14px; }",
268
+ automatic_uploads: !!uploader,
269
+ paste_data_images: !!uploader,
270
+ images_file_types: "jpeg,jpg,png,gif,webp",
271
+ file_picker_types: uploader ? "image" : "",
272
+ images_upload_handler: uploader ? (blobInfo) => uploader(new File([blobInfo.blob()], blobInfo.filename(), { type: blobInfo.blob().type })) : void 0
273
+ };
274
+ await tinymce.init({
275
+ ...defaults,
276
+ ...this.tinymceConfig(),
277
+ target: this.host.nativeElement,
278
+ base_url: this.loader.baseUrl,
279
+ inline,
280
+ setup: (editor) => {
281
+ editor.on("change input undo redo SetContent", () => {
282
+ if (this.writingValue || !this.editor) return;
283
+ this.value = editor.getContent();
284
+ this.onChange(this.value);
285
+ this.changeDetector.markForCheck();
286
+ });
287
+ editor.on("blur", () => {
288
+ this.onTouched();
289
+ this.changeDetector.markForCheck();
290
+ });
291
+ editor.on("FullscreenStateChanged", (event) => {
292
+ this.handleFullscreenChanged(event.state === true);
293
+ });
294
+ editor.on("keydown", (event) => {
295
+ if (!this.shouldEscapeLeaveFullscreen(event)) return;
296
+ event.preventDefault();
297
+ event.stopPropagation();
298
+ this.exitFullscreen();
299
+ });
300
+ editor.on("init", () => {
301
+ this.editor = editor;
302
+ this.applyValue(this.value);
303
+ this.applyDisabled(this.disabled);
304
+ this.ready.emit(editor);
305
+ this.changeDetector.markForCheck();
306
+ });
307
+ }
308
+ });
309
+ } catch (error) {
310
+ if (this.destroyed) return;
311
+ this.failed.emit(error instanceof Error ? error : new Error(String(error)));
312
+ this.changeDetector.markForCheck();
313
+ }
314
+ }
315
+ ngOnDestroy() {
316
+ this.destroyed = true;
317
+ this.releaseFullscreen();
318
+ this.editor?.remove();
319
+ this.editor = void 0;
320
+ }
321
+ shouldEscapeLeaveFullscreen(event) {
322
+ return event.key === "Escape" && this.fullscreen;
323
+ }
324
+ exitFullscreen() {
325
+ if (!this.fullscreen) return;
326
+ this.editor?.execCommand("mceFullScreen");
327
+ }
328
+ handleFullscreenChanged(state) {
329
+ if (state === this.fullscreen) return;
330
+ this.fullscreen = state;
331
+ if (state) document.addEventListener("keydown", this.onDocumentKeydown, true);
332
+ else this.releaseFullscreen();
333
+ this.changeDetector.markForCheck();
334
+ }
335
+ releaseFullscreen() {
336
+ document.removeEventListener("keydown", this.onDocumentKeydown, true);
337
+ this.fullscreen = false;
338
+ }
339
+ writeValue(value) {
340
+ this.value = value ?? "";
341
+ this.applyValue(this.value);
342
+ }
343
+ registerOnChange(fn) {
344
+ this.onChange = fn;
345
+ }
346
+ registerOnTouched(fn) {
347
+ this.onTouched = fn;
348
+ }
349
+ setDisabledState(isDisabled) {
350
+ this.disabled = isDisabled;
351
+ this.applyDisabled(isDisabled);
352
+ }
353
+ applyValue(value) {
354
+ const editor = this.editor;
355
+ if (!editor) return;
356
+ if (editor.getContent() === value) return;
357
+ this.writingValue = true;
358
+ try {
359
+ editor.setContent(value);
360
+ } finally {
361
+ this.writingValue = false;
362
+ }
363
+ }
364
+ applyDisabled(isDisabled) {
365
+ this.editor?.mode.set(isDisabled ? "readonly" : "design");
366
+ }
367
+ static {
368
+ this.ɵfac = i0.ɵɵngDeclareFactory({
369
+ minVersion: "12.0.0",
370
+ version: "22.2.1",
371
+ ngImport: i0,
372
+ type: TinymceEditorDirective,
373
+ deps: [],
374
+ target: i0.ɵɵFactoryTarget.Directive
375
+ });
376
+ }
377
+ static {
378
+ this.ɵdir = i0.ɵɵngDeclareDirective({
379
+ minVersion: "17.1.0",
380
+ version: "22.2.1",
381
+ type: TinymceEditorDirective,
382
+ isStandalone: true,
383
+ selector: "textarea[tinymceEditor], div[tinymceEditor]",
384
+ inputs: {
385
+ tinymceConfig: {
386
+ classPropertyName: "tinymceConfig",
387
+ publicName: "tinymceConfig",
388
+ isSignal: true,
389
+ isRequired: false,
390
+ transformFunction: null
391
+ },
392
+ tinymceToolbarMode: {
393
+ classPropertyName: "tinymceToolbarMode",
394
+ publicName: "tinymceToolbarMode",
395
+ isSignal: true,
396
+ isRequired: false,
397
+ transformFunction: null
398
+ },
399
+ tinymceImageUploader: {
400
+ classPropertyName: "tinymceImageUploader",
401
+ publicName: "tinymceImageUploader",
402
+ isSignal: true,
403
+ isRequired: false,
404
+ transformFunction: null
405
+ }
406
+ },
407
+ outputs: {
408
+ ready: "ready",
409
+ failed: "failed"
410
+ },
411
+ providers: [{
412
+ provide: NG_VALUE_ACCESSOR,
413
+ useExisting: forwardRef(() => TinymceEditorDirective),
414
+ multi: true
415
+ }],
416
+ ngImport: i0
417
+ });
418
+ }
419
+ };
420
+ i0.ɵɵngDeclareClassMetadata({
421
+ minVersion: "12.0.0",
422
+ version: "22.2.1",
423
+ ngImport: i0,
424
+ type: TinymceEditorDirective,
425
+ decorators: [{
426
+ type: Directive,
427
+ args: [{
428
+ selector: "textarea[tinymceEditor], div[tinymceEditor]",
429
+ providers: [{
430
+ provide: NG_VALUE_ACCESSOR,
431
+ useExisting: forwardRef(() => TinymceEditorDirective),
432
+ multi: true
433
+ }]
434
+ }]
435
+ }],
436
+ propDecorators: {
437
+ tinymceConfig: [{
438
+ type: i0.Input,
439
+ args: [{
440
+ isSignal: true,
441
+ alias: "tinymceConfig",
442
+ required: false
443
+ }]
444
+ }],
445
+ tinymceToolbarMode: [{
446
+ type: i0.Input,
447
+ args: [{
448
+ isSignal: true,
449
+ alias: "tinymceToolbarMode",
450
+ required: false
451
+ }]
452
+ }],
453
+ tinymceImageUploader: [{
454
+ type: i0.Input,
455
+ args: [{
456
+ isSignal: true,
457
+ alias: "tinymceImageUploader",
458
+ required: false
459
+ }]
460
+ }],
461
+ ready: [{
462
+ type: i0.Output,
463
+ args: ["ready"]
464
+ }],
465
+ failed: [{
466
+ type: i0.Output,
467
+ args: ["failed"]
468
+ }]
469
+ }
470
+ });
471
+ var TinyMceEditorComponent = class TinyMceEditorComponent {
472
+ constructor() {
473
+ this.saving = output();
474
+ this.dialogRef = inject(MatDialogRef);
475
+ this.dialogData = signal((() => {
476
+ const data = inject(MAT_DIALOG_DATA) ?? {};
477
+ return {
478
+ ...data,
479
+ text: data.text ?? ""
480
+ };
481
+ })(), ...ngDevMode ? [{ debugName: "dialogData" }] : /* istanbul ignore next */ []);
482
+ this.disabled = signal(false, ...ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []);
483
+ this.error = signal(void 0, ...ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []);
484
+ this.text = signal("", ...ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []);
485
+ this.textForm = form(this.text, (p) => {
486
+ disabled(p, () => this.disabled());
487
+ });
488
+ this.tinymceConfig = {};
489
+ const data = this.dialogData();
490
+ this.text.set(data.text);
491
+ this.tinymceConfig = data.configuration ? { ...data.configuration } : TinymceUtils.TinymceCompactExtendedConfig;
492
+ this.tinymceConfig.toolbar = ["undo redo | quickimage insertgroup | formatgroup paragraphgroup | code"];
493
+ this.disabled.set(data.disabled ?? false);
494
+ }
495
+ failed(error) {
496
+ console.error("[TinyMceEditorComponent] editor non disponibile", error);
497
+ this.error.set(error?.message || "Editor non disponibile.");
498
+ }
499
+ ok() {
500
+ this.saving.emit(this.text() ?? "");
501
+ setTimeout(() => {
502
+ this.dialogRef.close();
503
+ }, 500);
504
+ }
505
+ static {
506
+ this.ɵfac = i0.ɵɵngDeclareFactory({
507
+ minVersion: "12.0.0",
508
+ version: "22.2.1",
509
+ ngImport: i0,
510
+ type: TinyMceEditorComponent,
511
+ deps: [],
512
+ target: i0.ɵɵFactoryTarget.Component
513
+ });
514
+ }
515
+ static {
516
+ this.ɵcmp = i0.ɵɵngDeclareComponent({
517
+ minVersion: "17.0.0",
518
+ version: "22.2.1",
519
+ type: TinyMceEditorComponent,
520
+ isStandalone: true,
521
+ selector: "ng-component",
522
+ outputs: { saving: "saving" },
523
+ ngImport: i0,
524
+ template: "<mat-dialog-content style=\"padding: 10px 10px 0 10px\">\n @if (error()) {\n <!-- The textarea is gone with the editor that could not replace it: left in place it would be a\n plain box the form does not write into, which is the empty editor this message exists to\n explain. -->\n <div class=\"editor-error\">\n <div class=\"editor-error-title\">Non è stato possibile aprire l'editor.</div>\n <div class=\"editor-error-detail\">{{ error() }}</div>\n <div class=\"editor-error-detail\">Il testo non è stato modificato: chiudi e riprova.</div>\n </div>\n } @else {\n <textarea tinymceEditor class=\"full-screen-editor\" [tinymceConfig]=\"tinymceConfig\"\n [formField]=\"textForm\" (failed)=\"failed($event)\"></textarea>\n }\n</mat-dialog-content>\n<mat-dialog-actions>\n <div fxLayout=\"row\" fxLayoutGap=\"10px\" fxLayoutAlign=\"start center\" fxFill>\n <div fxFlex=\"50\">\n @if(dialogData().onShowInfo) {\n <button mat-stroked-button (click)=\"dialogData().onShowInfo()\">{{dialogData().infoButtonLabel ?? 'Informazioni'}}\n </button>\n }\n </div>\n <div fxFlex=\"50\" fxLayoutAlign=\"end\">\n <!-- Nothing to save when there was no editor: the only way out is the one that changes\n nothing. -->\n @if (disabled() || error()) {\n <button mat-stroked-button [mat-dialog-close]=\"true\">Chiudi</button>\n } @else {\n <button mat-flat-button (click)=\"ok()\">Salva</button>\n <button mat-stroked-button [mat-dialog-close]=\"true\">Annulla</button>\n }\n </div>\n </div>\n</mat-dialog-actions>\n",
525
+ styles: [".mat-mdc-dialog-content{height:calc(100% - 52px)!important;max-height:calc(100% - 52px)!important}::ng-deep .full-screen-editor+.tox-tinymce{height:100%!important}.editor-error{display:flex;flex-direction:column;gap:6px;justify-content:center;align-items:center;height:100%;min-height:120px;padding:24px;text-align:center}.editor-error .editor-error-title{font-weight:500}.editor-error .editor-error-detail{font-size:small;opacity:.8}\n"],
526
+ dependencies: [
527
+ {
528
+ kind: "directive",
529
+ type: MatDialogContent,
530
+ selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]"
531
+ },
532
+ {
533
+ kind: "directive",
534
+ type: TinymceEditorDirective,
535
+ selector: "textarea[tinymceEditor], div[tinymceEditor]",
536
+ inputs: [
537
+ "tinymceConfig",
538
+ "tinymceToolbarMode",
539
+ "tinymceImageUploader"
540
+ ],
541
+ outputs: ["ready", "failed"]
542
+ },
543
+ {
544
+ kind: "directive",
545
+ type: FormField,
546
+ selector: "[formField]",
547
+ inputs: ["formField"],
548
+ exportAs: ["formField"]
549
+ },
550
+ {
551
+ kind: "directive",
552
+ type: MatDialogActions,
553
+ selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]",
554
+ inputs: ["align"]
555
+ },
556
+ {
557
+ kind: "ngmodule",
558
+ type: FlexLayoutModule
559
+ },
560
+ {
561
+ kind: "directive",
562
+ type: i1.FxLayoutDirective,
563
+ selector: " [fxLayout], [fxLayout.xs], [fxLayout.sm], [fxLayout.md], [fxLayout.lg], [fxLayout.xl], [fxLayout.lt-sm], [fxLayout.lt-md], [fxLayout.lt-lg], [fxLayout.lt-xl], [fxLayout.gt-xs], [fxLayout.gt-sm], [fxLayout.gt-md], [fxLayout.gt-lg] ",
564
+ inputs: [
565
+ "fxLayout",
566
+ "fxLayout.xs",
567
+ "fxLayout.sm",
568
+ "fxLayout.md",
569
+ "fxLayout.lg",
570
+ "fxLayout.xl",
571
+ "fxLayout.lt-sm",
572
+ "fxLayout.lt-md",
573
+ "fxLayout.lt-lg",
574
+ "fxLayout.lt-xl",
575
+ "fxLayout.gt-xs",
576
+ "fxLayout.gt-sm",
577
+ "fxLayout.gt-md",
578
+ "fxLayout.gt-lg"
579
+ ]
580
+ },
581
+ {
582
+ kind: "directive",
583
+ type: i1.FxFlexDirective,
584
+ selector: " [fxFlex], [fxFlex.xs], [fxFlex.sm], [fxFlex.md], [fxFlex.lg], [fxFlex.xl], [fxFlex.lt-sm], [fxFlex.lt-md], [fxFlex.lt-lg], [fxFlex.lt-xl], [fxFlex.gt-xs], [fxFlex.gt-sm], [fxFlex.gt-md], [fxFlex.gt-lg] ",
585
+ inputs: [
586
+ "fxFlex",
587
+ "fxFlex.xs",
588
+ "fxFlex.sm",
589
+ "fxFlex.md",
590
+ "fxFlex.lg",
591
+ "fxFlex.xl",
592
+ "fxFlex.lt-sm",
593
+ "fxFlex.lt-md",
594
+ "fxFlex.lt-lg",
595
+ "fxFlex.lt-xl",
596
+ "fxFlex.gt-xs",
597
+ "fxFlex.gt-sm",
598
+ "fxFlex.gt-md",
599
+ "fxFlex.gt-lg"
600
+ ]
601
+ },
602
+ {
603
+ kind: "directive",
604
+ type: i1.FxLayoutAlignDirective,
605
+ selector: " [fxLayoutAlign], [fxLayoutAlign.xs], [fxLayoutAlign.sm], [fxLayoutAlign.md], [fxLayoutAlign.lg], [fxLayoutAlign.xl], [fxLayoutAlign.lt-sm], [fxLayoutAlign.lt-md], [fxLayoutAlign.lt-lg], [fxLayoutAlign.lt-xl], [fxLayoutAlign.gt-xs], [fxLayoutAlign.gt-sm], [fxLayoutAlign.gt-md], [fxLayoutAlign.gt-lg] ",
606
+ inputs: [
607
+ "fxLayoutAlign",
608
+ "fxLayoutAlign.xs",
609
+ "fxLayoutAlign.sm",
610
+ "fxLayoutAlign.md",
611
+ "fxLayoutAlign.lg",
612
+ "fxLayoutAlign.xl",
613
+ "fxLayoutAlign.lt-sm",
614
+ "fxLayoutAlign.lt-md",
615
+ "fxLayoutAlign.lt-lg",
616
+ "fxLayoutAlign.lt-xl",
617
+ "fxLayoutAlign.gt-xs",
618
+ "fxLayoutAlign.gt-sm",
619
+ "fxLayoutAlign.gt-md",
620
+ "fxLayoutAlign.gt-lg"
621
+ ]
622
+ },
623
+ {
624
+ kind: "directive",
625
+ type: i1.FxLayoutGapDirective,
626
+ selector: " [fxLayoutGap], [fxLayoutGap.xs], [fxLayoutGap.sm], [fxLayoutGap.md], [fxLayoutGap.lg], [fxLayoutGap.xl], [fxLayoutGap.lt-sm], [fxLayoutGap.lt-md], [fxLayoutGap.lt-lg], [fxLayoutGap.lt-xl], [fxLayoutGap.gt-xs], [fxLayoutGap.gt-sm], [fxLayoutGap.gt-md], [fxLayoutGap.gt-lg] ",
627
+ inputs: [
628
+ "fxLayoutGap",
629
+ "fxLayoutGap.xs",
630
+ "fxLayoutGap.sm",
631
+ "fxLayoutGap.md",
632
+ "fxLayoutGap.lg",
633
+ "fxLayoutGap.xl",
634
+ "fxLayoutGap.lt-sm",
635
+ "fxLayoutGap.lt-md",
636
+ "fxLayoutGap.lt-lg",
637
+ "fxLayoutGap.lt-xl",
638
+ "fxLayoutGap.gt-xs",
639
+ "fxLayoutGap.gt-sm",
640
+ "fxLayoutGap.gt-md",
641
+ "fxLayoutGap.gt-lg"
642
+ ]
643
+ },
644
+ {
645
+ kind: "directive",
646
+ type: i1.FxFlexFillDirective,
647
+ selector: " [fxFlexFill], [fxFill], [fxFlexFill.xs], [fxFlexFill.sm], [fxFlexFill.md], [fxFlexFill.lg], [fxFlexFill.xl], [fxFlexFill.lt-sm], [fxFlexFill.lt-md], [fxFlexFill.lt-lg], [fxFlexFill.lt-xl], [fxFlexFill.gt-xs], [fxFlexFill.gt-sm], [fxFlexFill.gt-md], [fxFlexFill.gt-lg] ",
648
+ inputs: [
649
+ "fxFlexFill",
650
+ "fxFill",
651
+ "fxFlexFill.xs",
652
+ "fxFlexFill.sm",
653
+ "fxFlexFill.md",
654
+ "fxFlexFill.lg",
655
+ "fxFlexFill.xl",
656
+ "fxFlexFill.lt-sm",
657
+ "fxFlexFill.lt-md",
658
+ "fxFlexFill.lt-lg",
659
+ "fxFlexFill.lt-xl",
660
+ "fxFlexFill.gt-xs",
661
+ "fxFlexFill.gt-sm",
662
+ "fxFlexFill.gt-md",
663
+ "fxFlexFill.gt-lg"
664
+ ]
665
+ },
666
+ {
667
+ kind: "ngmodule",
668
+ type: MatButtonModule
669
+ },
670
+ {
671
+ kind: "component",
672
+ type: i2.MatButton,
673
+ selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ",
674
+ inputs: ["matButton"],
675
+ exportAs: ["matButton", "matAnchor"]
676
+ },
677
+ {
678
+ kind: "directive",
679
+ type: MatDialogClose,
680
+ selector: "[mat-dialog-close], [matDialogClose]",
681
+ inputs: [
682
+ "aria-label",
683
+ "type",
684
+ "mat-dialog-close",
685
+ "matDialogClose"
686
+ ],
687
+ exportAs: ["matDialogClose"]
688
+ }
689
+ ],
690
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
691
+ });
692
+ }
693
+ };
694
+ i0.ɵɵngDeclareClassMetadata({
695
+ minVersion: "12.0.0",
696
+ version: "22.2.1",
697
+ ngImport: i0,
698
+ type: TinyMceEditorComponent,
699
+ decorators: [{
700
+ type: Component,
701
+ args: [{
702
+ standalone: true,
703
+ changeDetection: ChangeDetectionStrategy.OnPush,
704
+ imports: [
705
+ MatDialogContent,
706
+ TinymceEditorDirective,
707
+ FormField,
708
+ MatDialogActions,
709
+ FlexLayoutModule,
710
+ MatButtonModule,
711
+ MatDialogClose
712
+ ],
713
+ template: "<mat-dialog-content style=\"padding: 10px 10px 0 10px\">\n @if (error()) {\n <!-- The textarea is gone with the editor that could not replace it: left in place it would be a\n plain box the form does not write into, which is the empty editor this message exists to\n explain. -->\n <div class=\"editor-error\">\n <div class=\"editor-error-title\">Non è stato possibile aprire l'editor.</div>\n <div class=\"editor-error-detail\">{{ error() }}</div>\n <div class=\"editor-error-detail\">Il testo non è stato modificato: chiudi e riprova.</div>\n </div>\n } @else {\n <textarea tinymceEditor class=\"full-screen-editor\" [tinymceConfig]=\"tinymceConfig\"\n [formField]=\"textForm\" (failed)=\"failed($event)\"></textarea>\n }\n</mat-dialog-content>\n<mat-dialog-actions>\n <div fxLayout=\"row\" fxLayoutGap=\"10px\" fxLayoutAlign=\"start center\" fxFill>\n <div fxFlex=\"50\">\n @if(dialogData().onShowInfo) {\n <button mat-stroked-button (click)=\"dialogData().onShowInfo()\">{{dialogData().infoButtonLabel ?? 'Informazioni'}}\n </button>\n }\n </div>\n <div fxFlex=\"50\" fxLayoutAlign=\"end\">\n <!-- Nothing to save when there was no editor: the only way out is the one that changes\n nothing. -->\n @if (disabled() || error()) {\n <button mat-stroked-button [mat-dialog-close]=\"true\">Chiudi</button>\n } @else {\n <button mat-flat-button (click)=\"ok()\">Salva</button>\n <button mat-stroked-button [mat-dialog-close]=\"true\">Annulla</button>\n }\n </div>\n </div>\n</mat-dialog-actions>\n",
714
+ styles: [".mat-mdc-dialog-content{height:calc(100% - 52px)!important;max-height:calc(100% - 52px)!important}::ng-deep .full-screen-editor+.tox-tinymce{height:100%!important}.editor-error{display:flex;flex-direction:column;gap:6px;justify-content:center;align-items:center;height:100%;min-height:120px;padding:24px;text-align:center}.editor-error .editor-error-title{font-weight:500}.editor-error .editor-error-detail{font-size:small;opacity:.8}\n"]
715
+ }]
716
+ }],
717
+ ctorParameters: () => [],
718
+ propDecorators: { saving: [{
719
+ type: i0.Output,
720
+ args: ["saving"]
721
+ }] }
722
+ });
657
723
  export { TinyMceEditorComponent, TinymceEditorDirective, TinymceLoaderService, TinymceUtils };
658
- //# sourceMappingURL=arsedizioni-ars-utils-ui.tinymce.mjs.map
724
+
725
+ //# sourceMappingURL=arsedizioni-ars-utils-ui.tinymce.mjs.map