@stackch/angular-richtext-editor 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { EventEmitter, Output, Input, Component, forwardRef, HostListener, ViewChild } from '@angular/core';
2
+ import { EventEmitter, Output, Input, Component, HostListener, ViewChild, Directive, forwardRef } from '@angular/core';
3
3
  import { CommonModule } from '@angular/common';
4
4
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
5
5
 
@@ -126,6 +126,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
126
126
  type: Output
127
127
  }] } });
128
128
 
129
+ // ─── Config & Types ───────────────────────────────────────────────────────────
129
130
  class StackchRichtextEditorConfig {
130
131
  // Sichtbarkeit einzelner Toolbar-Elemente (Default: an)
131
132
  showUndoRedo = true;
@@ -183,12 +184,10 @@ class StackchRichtextEditorI18n {
183
184
  h5Label = 'H5';
184
185
  h6Label = 'H6';
185
186
  }
186
- // Predefined i18n bundles
187
+ // ─── Predefined i18n bundles ──────────────────────────────────────────────────
187
188
  // Consumers can import these and pass via config.i18n to localize the toolbar/labels.
188
189
  const STACKCH_RTE_I18N_DE = {
189
- // Generic
190
190
  placeholder: 'Schreiben…',
191
- // Toolbar titles
192
191
  undoTitle: 'Rückgängig (Strg+Z)',
193
192
  redoTitle: 'Wiederholen (Strg+Y)',
194
193
  fontPanelTitle: 'Schrift & Größe',
@@ -215,20 +214,13 @@ const STACKCH_RTE_I18N_DE = {
215
214
  alignJustifyTitle: 'Blocksatz',
216
215
  linkTitle: 'Link',
217
216
  removeFormatTitle: 'Formatierung entfernen',
218
- // Heading menu labels
219
217
  paragraphLabel: 'Absatz (P)',
220
218
  codeLabel: 'Code (pre)',
221
- h1Label: 'H1',
222
- h2Label: 'H2',
223
- h3Label: 'H3',
224
- h4Label: 'H4',
225
- h5Label: 'H5',
226
- h6Label: 'H6',
219
+ h1Label: 'H1', h2Label: 'H2', h3Label: 'H3',
220
+ h4Label: 'H4', h5Label: 'H5', h6Label: 'H6',
227
221
  };
228
222
  const STACKCH_RTE_I18N_FR = {
229
- // Generic
230
223
  placeholder: 'Écrire…',
231
- // Toolbar titles
232
224
  undoTitle: 'Annuler (Ctrl+Z)',
233
225
  redoTitle: 'Rétablir (Ctrl+Y)',
234
226
  fontPanelTitle: 'Police et taille',
@@ -255,20 +247,13 @@ const STACKCH_RTE_I18N_FR = {
255
247
  alignJustifyTitle: 'Justifié',
256
248
  linkTitle: 'Lien',
257
249
  removeFormatTitle: 'Effacer la mise en forme',
258
- // Heading menu labels
259
250
  paragraphLabel: 'Paragraphe (P)',
260
251
  codeLabel: 'Code (pre)',
261
- h1Label: 'H1',
262
- h2Label: 'H2',
263
- h3Label: 'H3',
264
- h4Label: 'H4',
265
- h5Label: 'H5',
266
- h6Label: 'H6',
252
+ h1Label: 'H1', h2Label: 'H2', h3Label: 'H3',
253
+ h4Label: 'H4', h5Label: 'H5', h6Label: 'H6',
267
254
  };
268
255
  const STACKCH_RTE_I18N_IT = {
269
- // Generic
270
256
  placeholder: 'Scrivi…',
271
- // Toolbar titles
272
257
  undoTitle: 'Annulla (Ctrl+Z)',
273
258
  redoTitle: 'Ripristina (Ctrl+Y)',
274
259
  fontPanelTitle: 'Carattere e dimensione',
@@ -295,18 +280,22 @@ const STACKCH_RTE_I18N_IT = {
295
280
  alignJustifyTitle: 'Giustificato',
296
281
  linkTitle: 'Collegamento',
297
282
  removeFormatTitle: 'Rimuovi formattazione',
298
- // Heading menu labels
299
283
  paragraphLabel: 'Paragrafo (P)',
300
284
  codeLabel: 'Codice (pre)',
301
- h1Label: 'H1',
302
- h2Label: 'H2',
303
- h3Label: 'H3',
304
- h4Label: 'H4',
305
- h5Label: 'H5',
306
- h6Label: 'H6',
285
+ h1Label: 'H1', h2Label: 'H2', h3Label: 'H3',
286
+ h4Label: 'H4', h5Label: 'H5', h6Label: 'H6',
307
287
  };
308
- class StackchRichtextEditor {
288
+ // ─── Abstract Base ────────────────────────────────────────────────────────────
289
+ /**
290
+ * Abstract base class shared by StackchRichtextEditor (core) and
291
+ * StackchRichtextEditorMaterial (Material variant).
292
+ * Contains all editing logic; concrete subclasses add only the @Component
293
+ * decorator with their own template / styles / toolbar.
294
+ */
295
+ class StackchRichtextEditorBase {
309
296
  cdr;
297
+ // Override in concrete class to distinguish log output
298
+ logPrefix = '[RTE]';
310
299
  placeholder = '';
311
300
  showToolbar = true;
312
301
  fonts = [
@@ -326,7 +315,6 @@ class StackchRichtextEditor {
326
315
  get disabled() { return this._disabled; }
327
316
  _disabled = false;
328
317
  valueChange = new EventEmitter();
329
- // Structured metrics event (future-proof)
330
318
  metricsChange = new EventEmitter();
331
319
  editorRef;
332
320
  constructor(cdr) {
@@ -362,7 +350,7 @@ class StackchRichtextEditor {
362
350
  isBoldActive = false;
363
351
  isItalicActive = false;
364
352
  isUnderlineActive = false;
365
- // Selection helpers
353
+ // ─── Selection helpers ───────────────────────────────────────────────────
366
354
  saveSelection() {
367
355
  const sel = window.getSelection();
368
356
  if (!sel || sel.rangeCount === 0)
@@ -375,7 +363,6 @@ class StackchRichtextEditor {
375
363
  }
376
364
  // Prevent toolbar buttons from stealing focus from the editor while preserving selection
377
365
  onToolbarMouseDown(evt) {
378
- // Keep focus on the editor to avoid persistent button focus outlines
379
366
  evt.preventDefault();
380
367
  evt.stopPropagation();
381
368
  this.saveSelection();
@@ -401,6 +388,7 @@ class StackchRichtextEditor {
401
388
  const node = container.nodeType === Node.ELEMENT_NODE ? container : container.parentElement;
402
389
  return !!node && editor.contains(node);
403
390
  }
391
+ // ─── Host listeners ──────────────────────────────────────────────────────
404
392
  // Update active-state on selection changes inside the editor
405
393
  onSelectionChange() {
406
394
  const sel = window.getSelection();
@@ -424,7 +412,7 @@ class StackchRichtextEditor {
424
412
  }
425
413
  // Keyboard: Undo/Redo
426
414
  onKeydown(evt) {
427
- const isMac = navigator.platform.toLowerCase().includes('mac');
415
+ const isMac = (navigator.userAgentData?.platform ?? navigator.platform).toLowerCase().includes('mac');
428
416
  const mod = isMac ? evt.metaKey : evt.ctrlKey;
429
417
  if (mod && !evt.shiftKey && (evt.key === 'z' || evt.key === 'Z')) {
430
418
  evt.preventDefault();
@@ -435,6 +423,7 @@ class StackchRichtextEditor {
435
423
  this.redo();
436
424
  }
437
425
  }
426
+ // ─── Menu toggles ────────────────────────────────────────────────────────
438
427
  toggleFontMenu(evt) {
439
428
  this.saveSelection();
440
429
  this.showFontMenu = !this.showFontMenu;
@@ -452,7 +441,6 @@ class StackchRichtextEditor {
452
441
  toggleFontPanel(evt) {
453
442
  this.saveSelection();
454
443
  this.showFontPanel = !this.showFontPanel;
455
- // close others if open
456
444
  if (this.showFontPanel) {
457
445
  this.showFontMenu = false;
458
446
  this.showSizeMenu = false;
@@ -531,6 +519,7 @@ class StackchRichtextEditor {
531
519
  }
532
520
  evt.stopPropagation();
533
521
  }
522
+ // ─── Pick handlers (toolbar → editor) ───────────────────────────────────
534
523
  onPickAlign(where) {
535
524
  this.focusEditor();
536
525
  this.restoreSelection();
@@ -583,7 +572,7 @@ class StackchRichtextEditor {
583
572
  this.emitValue();
584
573
  this.takeSnapshot('spacing');
585
574
  }
586
- // ControlValueAccessor
575
+ // ─── ControlValueAccessor ────────────────────────────────────────────────
587
576
  writeValue(value) {
588
577
  const el = this.editorRef?.nativeElement;
589
578
  if (!el)
@@ -599,12 +588,11 @@ class StackchRichtextEditor {
599
588
  registerOnChange(fn) { this.onChange = fn; }
600
589
  registerOnTouched(fn) { this.onTouched = fn; }
601
590
  setDisabledState(isDisabled) { this.disabled = isDisabled; }
591
+ // ─── Toolbar actions ─────────────────────────────────────────────────────
602
592
  // Toolbar actions using document.execCommand (deprecated but broadly supported)
603
593
  cmd(command, value) {
604
- // Restore last selection from editor before applying an action triggered by toolbar
605
594
  this.focusEditor();
606
595
  this.restoreSelection();
607
- // Bevorzugt: eigene Range-basierte Implementierungen
608
596
  switch (command) {
609
597
  case 'bold':
610
598
  this.applyInlineStyleSmart('fontWeight', 'bold');
@@ -669,7 +657,6 @@ class StackchRichtextEditor {
669
657
  }
670
658
  // Fallback (deprecated): nur für Format entfernen / Links lösen als Übergang
671
659
  try {
672
- // Hinweis im Dev-Mode ausgeben
673
660
  if (!('___rteWarned' in this)) {
674
661
  console.warn('[richtext-editor] document.execCommand ist deprecated; Fallback wird nur für removeFormat/unlink verwendet.');
675
662
  this.___rteWarned = true;
@@ -705,7 +692,6 @@ class StackchRichtextEditor {
705
692
  return;
706
693
  const r = sel.getRangeAt(0);
707
694
  if (r.collapsed) {
708
- // Firefox/Edge: wenn keine explizite Auswahl, erweitere auf nächstes Wort
709
695
  this.expandRangeToWord(r);
710
696
  }
711
697
  this.applySelectionStyles({ color });
@@ -729,7 +715,6 @@ class StackchRichtextEditor {
729
715
  const editor = this.editorRef.nativeElement;
730
716
  let node = range.startContainer;
731
717
  if (node.nodeType !== Node.TEXT_NODE) {
732
- // Versuche, einen Textknoten in der Nähe zu finden
733
718
  const walker = document.createTreeWalker(editor, NodeFilter.SHOW_TEXT);
734
719
  let found = null;
735
720
  while (walker.nextNode()) {
@@ -744,7 +729,6 @@ class StackchRichtextEditor {
744
729
  }
745
730
  const text = node.nodeType === Node.TEXT_NODE ? node.data : '';
746
731
  let start = 0, end = text.length;
747
- // einfache Wortgrenzen-Heuristik
748
732
  const pos = range.startOffset;
749
733
  for (let i = pos; i > 0; i--) {
750
734
  if (/\s/.test(text[i - 1])) {
@@ -788,7 +772,6 @@ class StackchRichtextEditor {
788
772
  this.updateInlineStates();
789
773
  }
790
774
  onPaste(evt) {
791
- // Optional: Clean paste to plain text while keeping basic formatting minimal.
792
775
  if (!evt.clipboardData)
793
776
  return;
794
777
  evt.preventDefault();
@@ -797,8 +780,8 @@ class StackchRichtextEditor {
797
780
  this.emitValue();
798
781
  this.takeSnapshot('paste');
799
782
  }
783
+ // ─── Internal emit helpers ───────────────────────────────────────────────
800
784
  emitValue() {
801
- // Clean up empty style attributes and redundant spans before emitting
802
785
  this.cleanupEmptyStylesAndSpans();
803
786
  const val = this.editorRef.nativeElement.innerHTML;
804
787
  this.onChange(val);
@@ -811,14 +794,12 @@ class StackchRichtextEditor {
811
794
  if (!editor)
812
795
  return;
813
796
  const htmlLen = editor.innerHTML.length;
814
- // textContent excludes markup; normalize null to empty string
815
797
  const textLen = (editor.textContent || '').length;
816
- // Structured metrics event
817
798
  this.metricsChange.emit({ htmlLength: htmlLen, textLength: textLen });
818
799
  }
819
800
  // Remove empty style attributes (style="") and unwrap spans without any attributes
820
801
  cleanupEmptyStylesAndSpans(root) {
821
- console.log('[RTE] cleanupEmptyStylesAndSpans: start cleanup');
802
+ console.log(`${this.logPrefix} cleanupEmptyStylesAndSpans: start cleanup`);
822
803
  const editor = root || this.editorRef.nativeElement;
823
804
  const toUnwrap = [];
824
805
  const toRemove = [];
@@ -826,7 +807,6 @@ class StackchRichtextEditor {
826
807
  while (walker.nextNode()) {
827
808
  const el = walker.currentNode;
828
809
  if (el.hasAttribute('style')) {
829
- // If no style declarations remain, drop the attribute
830
810
  const cssText = el.getAttribute('style') || '';
831
811
  const byApiEmpty = (el.style ? el.style.length === 0 : false);
832
812
  const normalized = cssText.replace(/[\s;]/g, '');
@@ -836,28 +816,28 @@ class StackchRichtextEditor {
836
816
  }
837
817
  if (el.tagName === 'SPAN') {
838
818
  if (!el.firstChild) {
839
- console.log('[RTE] cleanup: removing empty span', el);
819
+ console.log(`${this.logPrefix} cleanup: removing empty span`, el);
840
820
  toRemove.push(el);
841
821
  continue;
842
822
  }
843
823
  if (el.attributes.length === 0) {
844
- console.log('[RTE] cleanup: unwrapping style-free span', el);
824
+ console.log(`${this.logPrefix} cleanup: unwrapping style-free span`, el);
845
825
  toUnwrap.push(el);
846
826
  }
847
827
  }
848
828
  }
849
829
  for (const span of toUnwrap) {
850
- console.log('[RTE] cleanup: unwrap span', { span, childCount: span.childNodes.length });
830
+ console.log(`${this.logPrefix} cleanup: unwrap span`, { span, childCount: span.childNodes.length });
851
831
  while (span.firstChild)
852
832
  span.parentNode?.insertBefore(span.firstChild, span);
853
833
  span.remove();
854
834
  }
855
835
  for (const span of toRemove) {
856
- console.log('[RTE] cleanup: remove span without children', span);
836
+ console.log(`${this.logPrefix} cleanup: remove span without children`, span);
857
837
  span.remove();
858
838
  }
859
839
  }
860
- // History API
840
+ // ─── History API ─────────────────────────────────────────────────────────
861
841
  get canUndo() { return this.historyIndex > 0; }
862
842
  get canRedo() { return this.historyIndex >= 0 && this.historyIndex < this.history.length - 1; }
863
843
  undo() {
@@ -919,12 +899,10 @@ class StackchRichtextEditor {
919
899
  const editor = this.editorRef.nativeElement;
920
900
  this.isRestoringHistory = true;
921
901
  editor.innerHTML = snap.html;
922
- // Selektion wiederherstellen
923
902
  if (snap.range) {
924
903
  this.restoreSerializedRange(snap.range);
925
904
  }
926
905
  else {
927
- // Cursor ans Ende
928
906
  const sel = window.getSelection();
929
907
  if (sel) {
930
908
  sel.removeAllRanges();
@@ -936,7 +914,6 @@ class StackchRichtextEditor {
936
914
  }
937
915
  this.historyIndex = index;
938
916
  this.isRestoringHistory = false;
939
- // Werte emittieren nach Restore
940
917
  this.emitValue();
941
918
  }
942
919
  serializeRange(range) {
@@ -1010,13 +987,11 @@ class StackchRichtextEditor {
1010
987
  el.focus();
1011
988
  }
1012
989
  }
1013
- // Minimal inline style applier for inline styles
990
+ // ─── Inline style application ────────────────────────────────────────────
1014
991
  applyInlineStyle(cssProp, value) {
1015
- // Delegate to generic, style-agnostic applier
1016
992
  this.applySelectionStyles({ [cssProp]: value });
1017
993
  }
1018
994
  applyInlineStyleSmart(cssProp, value) {
1019
- // Delegate to generic applier which handles selection/logging
1020
995
  this.applySelectionStyles({ [cssProp]: value });
1021
996
  }
1022
997
  applySelectionStyles(styles) {
@@ -1024,21 +999,21 @@ class StackchRichtextEditor {
1024
999
  this.restoreSelection();
1025
1000
  const sel = window.getSelection();
1026
1001
  if (!sel || sel.rangeCount === 0) {
1027
- console.warn('[RTE] applySelectionStyles: no selection available', { styles });
1002
+ console.warn(`${this.logPrefix} applySelectionStyles: no selection available`, { styles });
1028
1003
  return;
1029
1004
  }
1030
1005
  let range = sel.getRangeAt(0);
1031
1006
  if (range.collapsed) {
1032
- console.warn('[RTE] applySelectionStyles: range is collapsed', { styles });
1007
+ console.warn(`${this.logPrefix} applySelectionStyles: range is collapsed`, { styles });
1033
1008
  return;
1034
1009
  }
1035
1010
  const entries = Object.entries(styles).filter(([, v]) => v != null && v !== '');
1036
1011
  if (entries.length === 0) {
1037
- console.warn('[RTE] applySelectionStyles: no style entries to apply', { styles });
1012
+ console.warn(`${this.logPrefix} applySelectionStyles: no style entries to apply`, { styles });
1038
1013
  return;
1039
1014
  }
1040
1015
  const propsToClear = Array.from(new Set(entries.map(([prop]) => this.toCssProperty(prop))));
1041
- console.log('[RTE] applySelectionStyles: clearing props before apply', { propsToClear, entries });
1016
+ console.log(`${this.logPrefix} applySelectionStyles: clearing props before apply`, { propsToClear, entries });
1042
1017
  for (const cssProp of propsToClear) {
1043
1018
  const beforeClear = range.cloneRange();
1044
1019
  const clearedRange = this.removeInlineStyleInRange(range, cssProp, { suppressEmit: true });
@@ -1050,24 +1025,24 @@ class StackchRichtextEditor {
1050
1025
  range = clearedRange;
1051
1026
  }
1052
1027
  if (range.collapsed && !beforeClear.collapsed) {
1053
- console.warn('[RTE] applySelectionStyles: range collapsed after clearing, restoring previous range');
1054
- const sel = window.getSelection();
1055
- if (sel) {
1056
- sel.removeAllRanges();
1057
- sel.addRange(beforeClear);
1028
+ console.warn(`${this.logPrefix} applySelectionStyles: range collapsed after clearing, restoring previous range`);
1029
+ const sel2 = window.getSelection();
1030
+ if (sel2) {
1031
+ sel2.removeAllRanges();
1032
+ sel2.addRange(beforeClear);
1058
1033
  }
1059
1034
  range = beforeClear;
1060
1035
  }
1061
1036
  }
1062
1037
  const segments = this.collectTextSegments(range);
1063
1038
  if (segments.length === 0) {
1064
- console.warn('[RTE] applySelectionStyles: no text segments found', {
1039
+ console.warn(`${this.logPrefix} applySelectionStyles: no text segments found`, {
1065
1040
  entries,
1066
1041
  commonAncestor: range.commonAncestorContainer
1067
1042
  });
1068
1043
  return;
1069
1044
  }
1070
- console.log('[RTE] applySelectionStyles: applying styles to segments', {
1045
+ console.log(`${this.logPrefix} applySelectionStyles: applying styles to segments`, {
1071
1046
  entries,
1072
1047
  segmentCount: segments.length
1073
1048
  });
@@ -1140,7 +1115,7 @@ class StackchRichtextEditor {
1140
1115
  return this.mergeAdjacentStyledSpans(wrapper);
1141
1116
  }
1142
1117
  catch (err) {
1143
- console.error('[RTE] applyStylesToSegment: failed to surround contents', {
1118
+ console.error(`${this.logPrefix} applyStylesToSegment: failed to surround contents`, {
1144
1119
  error: err,
1145
1120
  segment,
1146
1121
  entries
@@ -1206,7 +1181,6 @@ class StackchRichtextEditor {
1206
1181
  const frag = range.extractContents();
1207
1182
  el.appendChild(frag);
1208
1183
  range.insertNode(el);
1209
- // Auswahl hinter das Element setzen
1210
1184
  sel.removeAllRanges();
1211
1185
  const after = document.createRange();
1212
1186
  after.setStartAfter(el);
@@ -1221,14 +1195,13 @@ class StackchRichtextEditor {
1221
1195
  range.deleteContents();
1222
1196
  const node = document.createTextNode(text);
1223
1197
  range.insertNode(node);
1224
- // Cursor ans Ende des eingefügten Textes
1225
1198
  sel.removeAllRanges();
1226
1199
  const after = document.createRange();
1227
1200
  after.setStartAfter(node);
1228
1201
  after.collapse(true);
1229
1202
  sel.addRange(after);
1230
1203
  }
1231
- // ----- Inline toggle logic (Bold/Italic/Underline) -----
1204
+ // ─── Inline toggle logic (Bold / Italic / Underline) ─────────────────────
1232
1205
  updateInlineStates() {
1233
1206
  const sel = window.getSelection();
1234
1207
  if (!sel || sel.rangeCount === 0) {
@@ -1250,7 +1223,6 @@ class StackchRichtextEditor {
1250
1223
  this.isBoldActive = this.computeBoldAnyForRange(range);
1251
1224
  this.isItalicActive = this.computeItalicAnyForRange(range);
1252
1225
  this.isUnderlineActive = this.computeUnderlineAnyForRange(range);
1253
- // ensure UI reflects changes immediately
1254
1226
  try {
1255
1227
  this.cdr.detectChanges();
1256
1228
  }
@@ -1323,13 +1295,12 @@ class StackchRichtextEditor {
1323
1295
  }
1324
1296
  };
1325
1297
  const matchesEl = (el) => this.isStyleCarrier(el, cssPropKebab);
1326
- console.log('[RTE] removeInlineStyleInRange:start', {
1298
+ console.log(`${this.logPrefix} removeInlineStyleInRange:start`, {
1327
1299
  cssPropKebab,
1328
1300
  rangeCollapsed: effectiveRange.collapsed,
1329
1301
  commonTag: common.tagName,
1330
1302
  commonHasStyle: common.style ? common.style.getPropertyValue?.(cssPropKebab) : undefined
1331
1303
  });
1332
- // Include the common element itself if it matches and intersects (TreeWalker won't visit the root)
1333
1304
  if (common instanceof HTMLElement && matchesEl(common) && intersects(common)) {
1334
1305
  affected.push(common);
1335
1306
  }
@@ -1340,45 +1311,45 @@ class StackchRichtextEditor {
1340
1311
  continue;
1341
1312
  if (intersects(el)) {
1342
1313
  affected.push(el);
1343
- console.log('[RTE] removeInlineStyleInRange:match', el.tagName, el.getAttribute('style'));
1314
+ console.log(`${this.logPrefix} removeInlineStyleInRange:match`, el.tagName, el.getAttribute('style'));
1344
1315
  }
1345
1316
  }
1346
1317
  let removedTags = 0;
1347
1318
  let removedProps = 0;
1348
1319
  for (const el of affected) {
1349
1320
  if (el.tagName === 'B' || el.tagName === 'STRONG' || el.tagName === 'I' || el.tagName === 'EM' || el.tagName === 'U') {
1350
- console.log('[RTE] unwrap tag', el.tagName);
1321
+ console.log(`${this.logPrefix} unwrap tag`, el.tagName);
1351
1322
  while (el.firstChild)
1352
1323
  el.parentNode?.insertBefore(el.firstChild, el);
1353
1324
  el.remove();
1354
1325
  removedTags++;
1355
1326
  continue;
1356
1327
  }
1357
- console.log('[RTE] remove style', cssPropKebab, 'from', el.tagName, el.getAttribute('style'));
1328
+ console.log(`${this.logPrefix} remove style`, cssPropKebab, 'from', el.tagName, el.getAttribute('style'));
1358
1329
  el.style.removeProperty(cssPropKebab);
1359
1330
  if (!el.style.length) {
1360
- console.log('[RTE] removeInlineStyleInRange: style attribute now empty, removing attribute', el);
1331
+ console.log(`${this.logPrefix} removeInlineStyleInRange: style attribute now empty, removing attribute`, el);
1361
1332
  el.removeAttribute('style');
1362
1333
  }
1363
1334
  removedProps++;
1364
1335
  if (!el.getAttribute('style')) {
1365
- console.log('[RTE] unwrap empty styled span', el.tagName);
1336
+ console.log(`${this.logPrefix} unwrap empty styled span`, el.tagName);
1366
1337
  while (el.firstChild)
1367
1338
  el.parentNode?.insertBefore(el.firstChild, el);
1368
1339
  el.remove();
1369
1340
  }
1370
1341
  }
1371
1342
  this.pruneDanglingStyleCarriers(common, cssPropKebab);
1372
- console.log('[RTE] removeInlineStyleInRange:done', { affected: affected.length, removedTags, removedProps });
1343
+ console.log(`${this.logPrefix} removeInlineStyleInRange:done`, { affected: affected.length, removedTags, removedProps });
1373
1344
  let resultRange = null;
1374
1345
  if (!options?.suppressEmit) {
1375
1346
  this.emitValue();
1376
1347
  resultRange = null;
1377
1348
  }
1378
1349
  else {
1379
- const sel = window.getSelection();
1380
- if (sel && sel.rangeCount > 0) {
1381
- resultRange = sel.getRangeAt(0).cloneRange();
1350
+ const sel2 = window.getSelection();
1351
+ if (sel2 && sel2.rangeCount > 0) {
1352
+ resultRange = sel2.getRangeAt(0).cloneRange();
1382
1353
  }
1383
1354
  else if (shouldRestoreSelection) {
1384
1355
  resultRange = effectiveRange.cloneRange();
@@ -1409,11 +1380,7 @@ class StackchRichtextEditor {
1409
1380
  toRemove.push(el);
1410
1381
  }
1411
1382
  for (const el of toRemove) {
1412
- console.log('[RTE] pruneDanglingStyleCarriers: removing empty carrier', { tag: el.tagName, style: el.getAttribute('style') });
1413
- if (el.tagName === 'B' || el.tagName === 'STRONG' || el.tagName === 'I' || el.tagName === 'EM' || el.tagName === 'U') {
1414
- el.remove();
1415
- continue;
1416
- }
1383
+ console.log(`${this.logPrefix} pruneDanglingStyleCarriers: removing empty carrier`, { tag: el.tagName, style: el.getAttribute('style') });
1417
1384
  el.remove();
1418
1385
  }
1419
1386
  }
@@ -1558,7 +1525,7 @@ class StackchRichtextEditor {
1558
1525
  if (range.collapsed)
1559
1526
  this.expandRangeToWord(range);
1560
1527
  const anyActive = this.computeBoldAnyForRange(range);
1561
- console.log('[RTE] toggleBold', { anyActive, rangeCollapsed: range.collapsed });
1528
+ console.log(`${this.logPrefix} toggleBold`, { anyActive, rangeCollapsed: range.collapsed });
1562
1529
  if (anyActive) {
1563
1530
  this.removeInlineStyleInRange(range, 'font-weight');
1564
1531
  }
@@ -1606,6 +1573,7 @@ class StackchRichtextEditor {
1606
1573
  this.updateInlineStates();
1607
1574
  this.takeSnapshot('toggle-underline');
1608
1575
  }
1576
+ // ─── Block operations ─────────────────────────────────────────────────────
1609
1577
  getCurrentRange() {
1610
1578
  const sel = window.getSelection();
1611
1579
  if (!sel || sel.rangeCount === 0)
@@ -1619,7 +1587,6 @@ class StackchRichtextEditor {
1619
1587
  while (el && el.parentElement && el.parentElement !== editor) {
1620
1588
  el = el.parentElement;
1621
1589
  }
1622
- // Wenn direktes Kind des Editors
1623
1590
  if (el && el.parentElement === editor)
1624
1591
  return el;
1625
1592
  return editor;
@@ -1638,34 +1605,29 @@ class StackchRichtextEditor {
1638
1605
  if (!range)
1639
1606
  return;
1640
1607
  const editor = this.editorRef.nativeElement;
1641
- // Wenn bereits in einer Liste, Liste aufheben
1642
1608
  const listAncestor = this.findClosest(range.commonAncestorContainer, 'ul,ol');
1643
1609
  if (listAncestor) {
1644
- // unwrap: li-Inhalte an die Stelle der Liste setzen
1645
1610
  const frag = document.createDocumentFragment();
1646
- const items = Array.from(listAncestor.children); // li
1611
+ const items = Array.from(listAncestor.children);
1647
1612
  for (const li of items) {
1648
1613
  while (li.firstChild)
1649
1614
  frag.appendChild(li.firstChild);
1650
- // Optional: Zeilenumbruch zwischen Items
1651
1615
  frag.appendChild(document.createElement('br'));
1652
1616
  }
1653
1617
  listAncestor.replaceWith(frag);
1654
1618
  return;
1655
1619
  }
1656
- // Neue Liste um die aktuelle Auswahl legen
1657
1620
  const list = document.createElement(kind);
1658
1621
  const li = document.createElement('li');
1659
1622
  const contents = range.extractContents();
1660
1623
  if (!contents.hasChildNodes()) {
1661
- li.textContent = '\u200b'; // zero-width space um leere li zu vermeiden
1624
+ li.textContent = '\u200b';
1662
1625
  }
1663
1626
  else {
1664
1627
  li.appendChild(contents);
1665
1628
  }
1666
1629
  list.appendChild(li);
1667
1630
  range.insertNode(list);
1668
- // Cursor ins li setzen
1669
1631
  const sel = window.getSelection();
1670
1632
  if (sel) {
1671
1633
  sel.removeAllRanges();
@@ -1691,7 +1653,6 @@ class StackchRichtextEditor {
1691
1653
  applyAlign(startBlock);
1692
1654
  }
1693
1655
  else {
1694
- // grob: alle direkten Kinder zwischen start und end ausrichten
1695
1656
  const children = Array.from(editor.children);
1696
1657
  const i1 = children.indexOf(startBlock);
1697
1658
  const i2 = children.indexOf(endBlock);
@@ -1745,8 +1706,8 @@ class StackchRichtextEditor {
1745
1706
  }
1746
1707
  }
1747
1708
  }
1748
- // Versucht, Margin/Padding auf die konkrete Text-Selektion anzuwenden, indem die Auswahl mit einem Span umschlossen wird.
1749
- // Liefert true, wenn inline angewendet werden konnte; sonst false (z. B. wenn Auswahl blockübergreifend ist).
1709
+ // Versucht, Margin/Padding auf die konkrete Text-Selektion anzuwenden (inline Wrapper).
1710
+ // Liefert true, wenn inline angewendet werden konnte; sonst false.
1750
1711
  applySpacingToSelection(kind, target, value) {
1751
1712
  const sel = window.getSelection();
1752
1713
  if (!sel || sel.rangeCount === 0)
@@ -1756,7 +1717,6 @@ class StackchRichtextEditor {
1756
1717
  return false;
1757
1718
  const startBlock = this.getEditorChildAncestor(range.startContainer);
1758
1719
  const endBlock = this.getEditorChildAncestor(range.endContainer);
1759
- // Nur inline, wenn innerhalb desselben Blocks
1760
1720
  if (!startBlock || !endBlock || startBlock !== endBlock)
1761
1721
  return false;
1762
1722
  const wrapper = document.createElement('span');
@@ -1772,15 +1732,12 @@ class StackchRichtextEditor {
1772
1732
  wrapper.style[`${kind}Left`] = px;
1773
1733
  wrapper.style[`${kind}Right`] = px;
1774
1734
  }
1775
- // Für vertikale Margins auf Inline-Elementen sicherstellen, dass sie greifen
1776
1735
  if (kind === 'margin' && (target === 'all' || target === 'vertical')) {
1777
1736
  wrapper.style.display = 'inline-block';
1778
1737
  }
1779
- // Auswahl extrahieren und in Wrapper einsetzen
1780
1738
  const frag = range.extractContents();
1781
1739
  wrapper.appendChild(frag);
1782
1740
  range.insertNode(wrapper);
1783
- // Gleichartige Wrapper zusammenführen und Cursor korrekt setzen
1784
1741
  const normalized = this.normalizeSpacingSpans(wrapper, kind, target, value) || wrapper;
1785
1742
  sel.removeAllRanges();
1786
1743
  const after = document.createRange();
@@ -1788,7 +1745,6 @@ class StackchRichtextEditor {
1788
1745
  after.setStartAfter(normalized);
1789
1746
  }
1790
1747
  else {
1791
- // Fallback: an das Ende des Startblocks
1792
1748
  const sb = this.getEditorChildAncestor(range.startContainer);
1793
1749
  if (sb)
1794
1750
  after.selectNodeContents(sb);
@@ -1797,7 +1753,7 @@ class StackchRichtextEditor {
1797
1753
  sel.addRange(after);
1798
1754
  return true;
1799
1755
  }
1800
- normalizeSpacingSpans(span, kind, target, value) {
1756
+ normalizeSpacingSpans(span, kind, target, _value) {
1801
1757
  const props = [];
1802
1758
  if (target === 'all')
1803
1759
  props.push(kind);
@@ -1806,7 +1762,6 @@ class StackchRichtextEditor {
1806
1762
  if (target === 'horizontal')
1807
1763
  props.push(`${kind}Left`, `${kind}Right`);
1808
1764
  const displayNeeded = kind === 'margin' && (target === 'all' || target === 'vertical');
1809
- const v = `${value}px`;
1810
1765
  const hasSameSpacing = (a, b) => {
1811
1766
  for (const p of props) {
1812
1767
  if (a.style[p] !== b.style[p])
@@ -1818,7 +1773,6 @@ class StackchRichtextEditor {
1818
1773
  }
1819
1774
  return true;
1820
1775
  };
1821
- // Downward: verschachtelte identische Spans in span zusammenführen
1822
1776
  if (span.children.length === 1) {
1823
1777
  const only = span.children[0];
1824
1778
  if (only && only.tagName === 'SPAN' && hasSameSpacing(span, only)) {
@@ -1828,7 +1782,6 @@ class StackchRichtextEditor {
1828
1782
  }
1829
1783
  }
1830
1784
  let current = span;
1831
- // Upward: mit Elternelement verschmelzen, wenn identischer Span
1832
1785
  const parent = current.parentElement;
1833
1786
  if (parent && parent.tagName === 'SPAN' && hasSameSpacing(parent, current)) {
1834
1787
  while (current.firstChild)
@@ -1838,7 +1791,6 @@ class StackchRichtextEditor {
1838
1791
  }
1839
1792
  if (!current)
1840
1793
  return null;
1841
- // Left merge: vorherige Geschwister-Spans mit gleicher Formatierung in current ziehen
1842
1794
  let prev = current.previousElementSibling;
1843
1795
  if (prev && prev.tagName === 'SPAN' && hasSameSpacing(prev, current)) {
1844
1796
  while (current.firstChild)
@@ -1846,7 +1798,6 @@ class StackchRichtextEditor {
1846
1798
  current.remove();
1847
1799
  current = prev;
1848
1800
  }
1849
- // Right merge: folgende Geschwister-Spans in current ziehen
1850
1801
  let next = current.nextElementSibling;
1851
1802
  while (next && next.tagName === 'SPAN' && hasSameSpacing(current, next)) {
1852
1803
  while (next.firstChild)
@@ -1862,6 +1813,7 @@ class StackchRichtextEditor {
1862
1813
  if (!range)
1863
1814
  return;
1864
1815
  const editor = this.editorRef.nativeElement;
1816
+ const PRE_STYLE = 'white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;';
1865
1817
  const replaceTag = (el, newTag) => {
1866
1818
  if (!el || el === editor)
1867
1819
  return null;
@@ -1873,24 +1825,23 @@ class StackchRichtextEditor {
1873
1825
  };
1874
1826
  const startBlock = this.getEditorChildAncestor(range.startContainer);
1875
1827
  const endBlock = this.getEditorChildAncestor(range.endContainer);
1876
- if (startBlock && startBlock !== editor && (startBlock === endBlock)) {
1828
+ if (startBlock && startBlock !== editor && startBlock === endBlock) {
1877
1829
  const currentTag = (startBlock.tagName || '').toLowerCase();
1878
1830
  if (currentTag === tag)
1879
1831
  return;
1880
1832
  const convertible = /^(p|div|pre|h1|h2|h3|h4|h5|h6)$/i.test(currentTag);
1881
1833
  if (convertible) {
1882
1834
  const neo = replaceTag(startBlock, tag);
1883
- if (neo && tag === 'pre') {
1884
- neo.setAttribute('style', 'white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;');
1885
- }
1835
+ if (neo && tag === 'pre')
1836
+ neo.setAttribute('style', PRE_STYLE);
1886
1837
  if (neo) {
1887
1838
  const sel = window.getSelection();
1888
1839
  if (sel) {
1889
1840
  sel.removeAllRanges();
1890
- const after = document.createRange();
1891
- after.selectNodeContents(neo);
1892
- after.collapse(false);
1893
- sel.addRange(after);
1841
+ const r = document.createRange();
1842
+ r.selectNodeContents(neo);
1843
+ r.collapse(false);
1844
+ sel.addRange(r);
1894
1845
  }
1895
1846
  }
1896
1847
  return;
@@ -1905,12 +1856,10 @@ class StackchRichtextEditor {
1905
1856
  let last = null;
1906
1857
  for (let i = from; i <= to; i++) {
1907
1858
  const el = children[i];
1908
- const currentTag = (el.tagName || '').toLowerCase();
1909
- if (/^(p|div|pre|h1|h2|h3|h4|h5|h6)$/i.test(currentTag)) {
1859
+ if (/^(p|div|pre|h1|h2|h3|h4|h5|h6)$/i.test(el.tagName)) {
1910
1860
  const neo = replaceTag(el, tag);
1911
- if (neo && tag === 'pre') {
1912
- neo.setAttribute('style', 'white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;');
1913
- }
1861
+ if (neo && tag === 'pre')
1862
+ neo.setAttribute('style', PRE_STYLE);
1914
1863
  if (neo)
1915
1864
  last = neo;
1916
1865
  }
@@ -1919,10 +1868,10 @@ class StackchRichtextEditor {
1919
1868
  const sel = window.getSelection();
1920
1869
  if (sel) {
1921
1870
  sel.removeAllRanges();
1922
- const after = document.createRange();
1923
- after.selectNodeContents(last);
1924
- after.collapse(false);
1925
- sel.addRange(after);
1871
+ const r = document.createRange();
1872
+ r.selectNodeContents(last);
1873
+ r.collapse(false);
1874
+ sel.addRange(r);
1926
1875
  }
1927
1876
  }
1928
1877
  return;
@@ -1930,40 +1879,26 @@ class StackchRichtextEditor {
1930
1879
  }
1931
1880
  if (!range.collapsed) {
1932
1881
  const el = document.createElement(tag);
1933
- if (tag === 'pre') {
1934
- el.setAttribute('style', 'white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;');
1935
- }
1882
+ if (tag === 'pre')
1883
+ el.setAttribute('style', PRE_STYLE);
1936
1884
  const frag = range.extractContents();
1937
1885
  el.appendChild(frag);
1938
1886
  range.insertNode(el);
1939
1887
  const sel = window.getSelection();
1940
1888
  if (sel) {
1941
1889
  sel.removeAllRanges();
1942
- const after = document.createRange();
1943
- after.selectNodeContents(el);
1944
- after.collapse(false);
1945
- sel.addRange(after);
1890
+ const r = document.createRange();
1891
+ r.selectNodeContents(el);
1892
+ r.collapse(false);
1893
+ sel.addRange(r);
1946
1894
  }
1947
1895
  }
1948
1896
  }
1949
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StackchRichtextEditor, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1950
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StackchRichtextEditor, isStandalone: true, selector: "stackch-richtext-editor", inputs: { placeholder: "placeholder", showToolbar: "showToolbar", fonts: "fonts", fontSizes: "fontSizes", height: "height", minHeight: "minHeight", maxHeight: "maxHeight", disabled: "disabled", config: "config" }, outputs: { valueChange: "valueChange", metricsChange: "metricsChange" }, host: { listeners: { "document:selectionchange": "onSelectionChange()", "document:click": "closeMenus()", "keydown": "onKeydown($event)" } }, providers: [
1951
- {
1952
- provide: NG_VALUE_ACCESSOR,
1953
- useExisting: forwardRef(() => StackchRichtextEditor),
1954
- multi: true,
1955
- },
1956
- ], viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"stackch_rte\" [class.stackch_rte--disabled]=\"disabled\">\r\n @if (showToolbar) {\r\n <stackch-richtext-editor-toolbar\r\n [cfg]=\"cfg\"\r\n [i18n]=\"i18n\"\r\n [disabled]=\"disabled\"\r\n [fonts]=\"fonts\"\r\n [fontSizes]=\"fontSizes\"\r\n [isBoldActive]=\"isBoldActive\"\r\n [isItalicActive]=\"isItalicActive\"\r\n [isUnderlineActive]=\"isUnderlineActive\"\r\n [canUndo]=\"canUndo\"\r\n [canRedo]=\"canRedo\"\r\n [uiState]=\"{ showFontPanel: showFontPanel, showHeadingMenu: showHeadingMenu, showSpacingMenu: showSpacingMenu, showAlignMenu: showAlignMenu, showColorMenu: showColorMenu, showListMenu: showListMenu }\"\r\n\r\n (undo)=\"undo()\"\r\n (redo)=\"redo()\"\r\n (toggleFontPanel)=\"toggleFontPanel($event)\"\r\n (toggleHeadingMenu)=\"toggleHeadingMenu($event)\"\r\n (toggleSpacingMenu)=\"toggleSpacingMenu($event)\"\r\n (toggleAlignMenu)=\"toggleAlignMenu($event)\"\r\n (toggleColorMenu)=\"toggleColorMenu($event)\"\r\n (toggleListMenu)=\"toggleListMenu($event)\"\r\n (pickFont)=\"onPickFont($any($event))\"\r\n (pickFontSize)=\"onPickFontSize($any($event))\"\r\n (pickAlign)=\"onPickAlign($any($event))\"\r\n (pickList)=\"onPickList($any($event))\"\r\n (pickHeading)=\"onPickHeading($any($event))\"\r\n (pickSpacing)=\"onPickSpacing($any($event).kind, $any($event).target, $any($event).value)\"\r\n (applyColor)=\"applyColor($any($event))\"\r\n (applyHighlight)=\"applyHighlight($any($event))\"\r\n (toggleBold)=\"toggleBold()\"\r\n (toggleItalic)=\"toggleItalic()\"\r\n (toggleUnderline)=\"toggleUnderline()\"\r\n (insertLink)=\"insertLink()\"\r\n (removeFormat)=\"removeFormat()\"\r\n (saveSelectionRequest)=\"saveSelection()\"\r\n />\r\n }\r\n\r\n <div #editor\r\n class=\"stackch_rte__editor\"\r\n [attr.contenteditable]=\"!disabled\"\r\n [attr.data-placeholder]=\"placeholder || i18n.placeholder\"\r\n [style.minHeight.px]=\"minHeight\"\r\n [style.maxHeight.px]=\"maxHeight\"\r\n [style.height.px]=\"height\"\r\n (mouseup)=\"saveSelection()\"\r\n (keyup)=\"onKeyup($event)\"\r\n (input)=\"onInput()\"\r\n (blur)=\"onTouched()\"\r\n (paste)=\"onPaste($event)\"></div>\r\n</div>\r\n", styles: [".stackch_rte{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,sans-serif}.stackch_rte--disabled{opacity:.7;pointer-events:none}.stackch_rte__toolbar{display:flex;flex-wrap:wrap;gap:.25rem;align-items:center;border:1px solid #d0d7de;border-bottom:none;background:#f6f8fa;padding:.25rem;border-radius:6px 6px 0 0}.stackch_rte__btn{appearance:none;border:1px solid #d0d7de;background:#fff;padding:.25rem .5rem;border-radius:4px;cursor:pointer;transition:background .12s ease,border-color .12s ease,color .12s ease;outline:none}.stackch_rte__btn:hover{background:#f3f4f6}.stackch_rte__btn:focus{outline:none;box-shadow:none}.stackch_rte__btn:focus-visible{outline:2px solid rgba(9,105,218,.2)}.stackch_rte__btn.is-active{background:#e7f3ff;border-color:#0969da;color:#084298}.stackch_rte__select{border:1px solid #d0d7de;border-radius:4px;padding:.2rem .4rem;background:#fff}.stackch_rte__color{display:inline-flex;align-items:center;gap:.25rem;border:1px solid #d0d7de;padding:0 .4rem;border-radius:4px;background:#fff}.stackch_rte__color>input{inline-size:1.75rem;block-size:1.5rem;padding:0;border:none;background:none}.stackch_rte__sep{width:1px;height:1.25rem;background:#d0d7de;margin:0 .125rem}.stackch_rte__editor{border:1px solid #d0d7de;border-radius:0 0 6px 6px;padding:.5rem;background:#fff;overflow:auto}.stackch_rte__editor:empty:before{content:attr(data-placeholder);color:#97a1ad;pointer-events:none}.stackch_rte__editor:focus{outline:none;box-shadow:inset 0 0 0 1px #0969da;border-color:#0969da}.stackch_rte__dropdown{position:relative;display:inline-block}.stackch_rte__menu{position:absolute;top:100%;left:0;z-index:2;background:#fff;border:1px solid #d0d7de;border-radius:6px;box-shadow:0 4px 12px #00000014;padding:.25rem;min-width:220px;max-height:220px;overflow:auto}.stackch_rte__menu-item{display:block;width:100%;text-align:left;background:#fff;border:none;padding:.375rem .5rem;border-radius:4px;cursor:pointer}.stackch_rte__menu-item:hover{background:#f3f4f6}.stackch_rte__menu--row{display:flex;flex-direction:row;gap:.25rem;align-items:center;min-width:auto;max-height:none;flex-wrap:nowrap}.stackch_rte__menu--row .stackch_rte__menu-item{display:inline-flex;width:auto;text-align:center;justify-content:center;align-items:center;padding:.3rem .45rem;white-space:nowrap;flex:0 0 auto;word-break:keep-all;overflow-wrap:normal}.stackch_rte__menu--row .stackch_rte__menu-item[style*=\"width:24px\"]{padding:0;width:24px;height:24px}.stackch_rte__menu--grid{display:grid;grid-template-columns:1fr 1fr;gap:.5rem 1rem;min-width:420px}.stackch_rte__menu-section{display:flex;flex-direction:column;gap:.25rem}.stackch_rte__menu-title{font-size:12px;color:#57606a;padding:.25rem;text-transform:uppercase;letter-spacing:.04em}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: StackchRichtextEditorToolbar, selector: "stackch-richtext-editor-toolbar", inputs: ["cfg", "i18n", "disabled", "fonts", "fontSizes", "isBoldActive", "isItalicActive", "isUnderlineActive", "canUndo", "canRedo", "uiState"], outputs: ["undo", "redo", "toggleFontPanel", "toggleHeadingMenu", "toggleSpacingMenu", "toggleAlignMenu", "toggleColorMenu", "toggleListMenu", "pickFont", "pickFontSize", "pickAlign", "pickList", "pickHeading", "pickSpacing", "applyColor", "applyHighlight", "toggleBold", "toggleItalic", "toggleUnderline", "insertLink", "removeFormat", "saveSelectionRequest"] }] });
1897
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StackchRichtextEditorBase, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Directive });
1898
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.4", type: StackchRichtextEditorBase, isStandalone: true, inputs: { placeholder: "placeholder", showToolbar: "showToolbar", fonts: "fonts", fontSizes: "fontSizes", height: "height", minHeight: "minHeight", maxHeight: "maxHeight", disabled: "disabled", config: "config" }, outputs: { valueChange: "valueChange", metricsChange: "metricsChange" }, host: { listeners: { "document:selectionchange": "onSelectionChange()", "document:click": "closeMenus()", "keydown": "onKeydown($event)" } }, viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true, static: true }], ngImport: i0 });
1957
1899
  }
1958
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StackchRichtextEditor, decorators: [{
1959
- type: Component,
1960
- args: [{ selector: 'stackch-richtext-editor', standalone: true, imports: [CommonModule, StackchRichtextEditorToolbar], providers: [
1961
- {
1962
- provide: NG_VALUE_ACCESSOR,
1963
- useExisting: forwardRef(() => StackchRichtextEditor),
1964
- multi: true,
1965
- },
1966
- ], template: "<div class=\"stackch_rte\" [class.stackch_rte--disabled]=\"disabled\">\r\n @if (showToolbar) {\r\n <stackch-richtext-editor-toolbar\r\n [cfg]=\"cfg\"\r\n [i18n]=\"i18n\"\r\n [disabled]=\"disabled\"\r\n [fonts]=\"fonts\"\r\n [fontSizes]=\"fontSizes\"\r\n [isBoldActive]=\"isBoldActive\"\r\n [isItalicActive]=\"isItalicActive\"\r\n [isUnderlineActive]=\"isUnderlineActive\"\r\n [canUndo]=\"canUndo\"\r\n [canRedo]=\"canRedo\"\r\n [uiState]=\"{ showFontPanel: showFontPanel, showHeadingMenu: showHeadingMenu, showSpacingMenu: showSpacingMenu, showAlignMenu: showAlignMenu, showColorMenu: showColorMenu, showListMenu: showListMenu }\"\r\n\r\n (undo)=\"undo()\"\r\n (redo)=\"redo()\"\r\n (toggleFontPanel)=\"toggleFontPanel($event)\"\r\n (toggleHeadingMenu)=\"toggleHeadingMenu($event)\"\r\n (toggleSpacingMenu)=\"toggleSpacingMenu($event)\"\r\n (toggleAlignMenu)=\"toggleAlignMenu($event)\"\r\n (toggleColorMenu)=\"toggleColorMenu($event)\"\r\n (toggleListMenu)=\"toggleListMenu($event)\"\r\n (pickFont)=\"onPickFont($any($event))\"\r\n (pickFontSize)=\"onPickFontSize($any($event))\"\r\n (pickAlign)=\"onPickAlign($any($event))\"\r\n (pickList)=\"onPickList($any($event))\"\r\n (pickHeading)=\"onPickHeading($any($event))\"\r\n (pickSpacing)=\"onPickSpacing($any($event).kind, $any($event).target, $any($event).value)\"\r\n (applyColor)=\"applyColor($any($event))\"\r\n (applyHighlight)=\"applyHighlight($any($event))\"\r\n (toggleBold)=\"toggleBold()\"\r\n (toggleItalic)=\"toggleItalic()\"\r\n (toggleUnderline)=\"toggleUnderline()\"\r\n (insertLink)=\"insertLink()\"\r\n (removeFormat)=\"removeFormat()\"\r\n (saveSelectionRequest)=\"saveSelection()\"\r\n />\r\n }\r\n\r\n <div #editor\r\n class=\"stackch_rte__editor\"\r\n [attr.contenteditable]=\"!disabled\"\r\n [attr.data-placeholder]=\"placeholder || i18n.placeholder\"\r\n [style.minHeight.px]=\"minHeight\"\r\n [style.maxHeight.px]=\"maxHeight\"\r\n [style.height.px]=\"height\"\r\n (mouseup)=\"saveSelection()\"\r\n (keyup)=\"onKeyup($event)\"\r\n (input)=\"onInput()\"\r\n (blur)=\"onTouched()\"\r\n (paste)=\"onPaste($event)\"></div>\r\n</div>\r\n", styles: [".stackch_rte{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,sans-serif}.stackch_rte--disabled{opacity:.7;pointer-events:none}.stackch_rte__toolbar{display:flex;flex-wrap:wrap;gap:.25rem;align-items:center;border:1px solid #d0d7de;border-bottom:none;background:#f6f8fa;padding:.25rem;border-radius:6px 6px 0 0}.stackch_rte__btn{appearance:none;border:1px solid #d0d7de;background:#fff;padding:.25rem .5rem;border-radius:4px;cursor:pointer;transition:background .12s ease,border-color .12s ease,color .12s ease;outline:none}.stackch_rte__btn:hover{background:#f3f4f6}.stackch_rte__btn:focus{outline:none;box-shadow:none}.stackch_rte__btn:focus-visible{outline:2px solid rgba(9,105,218,.2)}.stackch_rte__btn.is-active{background:#e7f3ff;border-color:#0969da;color:#084298}.stackch_rte__select{border:1px solid #d0d7de;border-radius:4px;padding:.2rem .4rem;background:#fff}.stackch_rte__color{display:inline-flex;align-items:center;gap:.25rem;border:1px solid #d0d7de;padding:0 .4rem;border-radius:4px;background:#fff}.stackch_rte__color>input{inline-size:1.75rem;block-size:1.5rem;padding:0;border:none;background:none}.stackch_rte__sep{width:1px;height:1.25rem;background:#d0d7de;margin:0 .125rem}.stackch_rte__editor{border:1px solid #d0d7de;border-radius:0 0 6px 6px;padding:.5rem;background:#fff;overflow:auto}.stackch_rte__editor:empty:before{content:attr(data-placeholder);color:#97a1ad;pointer-events:none}.stackch_rte__editor:focus{outline:none;box-shadow:inset 0 0 0 1px #0969da;border-color:#0969da}.stackch_rte__dropdown{position:relative;display:inline-block}.stackch_rte__menu{position:absolute;top:100%;left:0;z-index:2;background:#fff;border:1px solid #d0d7de;border-radius:6px;box-shadow:0 4px 12px #00000014;padding:.25rem;min-width:220px;max-height:220px;overflow:auto}.stackch_rte__menu-item{display:block;width:100%;text-align:left;background:#fff;border:none;padding:.375rem .5rem;border-radius:4px;cursor:pointer}.stackch_rte__menu-item:hover{background:#f3f4f6}.stackch_rte__menu--row{display:flex;flex-direction:row;gap:.25rem;align-items:center;min-width:auto;max-height:none;flex-wrap:nowrap}.stackch_rte__menu--row .stackch_rte__menu-item{display:inline-flex;width:auto;text-align:center;justify-content:center;align-items:center;padding:.3rem .45rem;white-space:nowrap;flex:0 0 auto;word-break:keep-all;overflow-wrap:normal}.stackch_rte__menu--row .stackch_rte__menu-item[style*=\"width:24px\"]{padding:0;width:24px;height:24px}.stackch_rte__menu--grid{display:grid;grid-template-columns:1fr 1fr;gap:.5rem 1rem;min-width:420px}.stackch_rte__menu-section{display:flex;flex-direction:column;gap:.25rem}.stackch_rte__menu-title{font-size:12px;color:#57606a;padding:.25rem;text-transform:uppercase;letter-spacing:.04em}\n"] }]
1900
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StackchRichtextEditorBase, decorators: [{
1901
+ type: Directive
1967
1902
  }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }], propDecorators: { placeholder: [{
1968
1903
  type: Input
1969
1904
  }], showToolbar: [{
@@ -2000,6 +1935,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
2000
1935
  args: ['keydown', ['$event']]
2001
1936
  }] } });
2002
1937
 
1938
+ class StackchRichtextEditor extends StackchRichtextEditorBase {
1939
+ logPrefix = '[RTE]';
1940
+ constructor(cdr) {
1941
+ super(cdr);
1942
+ }
1943
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StackchRichtextEditor, deps: [{ token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
1944
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.4", type: StackchRichtextEditor, isStandalone: true, selector: "stackch-richtext-editor", providers: [
1945
+ {
1946
+ provide: NG_VALUE_ACCESSOR,
1947
+ useExisting: forwardRef(() => StackchRichtextEditor),
1948
+ multi: true,
1949
+ },
1950
+ ], usesInheritance: true, ngImport: i0, template: "<div class=\"stackch_rte\" [class.stackch_rte--disabled]=\"disabled\">\r\n @if (showToolbar) {\r\n <stackch-richtext-editor-toolbar\r\n [cfg]=\"cfg\"\r\n [i18n]=\"i18n\"\r\n [disabled]=\"disabled\"\r\n [fonts]=\"fonts\"\r\n [fontSizes]=\"fontSizes\"\r\n [isBoldActive]=\"isBoldActive\"\r\n [isItalicActive]=\"isItalicActive\"\r\n [isUnderlineActive]=\"isUnderlineActive\"\r\n [canUndo]=\"canUndo\"\r\n [canRedo]=\"canRedo\"\r\n [uiState]=\"{ showFontPanel: showFontPanel, showHeadingMenu: showHeadingMenu, showSpacingMenu: showSpacingMenu, showAlignMenu: showAlignMenu, showColorMenu: showColorMenu, showListMenu: showListMenu }\"\r\n\r\n (undo)=\"undo()\"\r\n (redo)=\"redo()\"\r\n (toggleFontPanel)=\"toggleFontPanel($event)\"\r\n (toggleHeadingMenu)=\"toggleHeadingMenu($event)\"\r\n (toggleSpacingMenu)=\"toggleSpacingMenu($event)\"\r\n (toggleAlignMenu)=\"toggleAlignMenu($event)\"\r\n (toggleColorMenu)=\"toggleColorMenu($event)\"\r\n (toggleListMenu)=\"toggleListMenu($event)\"\r\n (pickFont)=\"onPickFont($any($event))\"\r\n (pickFontSize)=\"onPickFontSize($any($event))\"\r\n (pickAlign)=\"onPickAlign($any($event))\"\r\n (pickList)=\"onPickList($any($event))\"\r\n (pickHeading)=\"onPickHeading($any($event))\"\r\n (pickSpacing)=\"onPickSpacing($any($event).kind, $any($event).target, $any($event).value)\"\r\n (applyColor)=\"applyColor($any($event))\"\r\n (applyHighlight)=\"applyHighlight($any($event))\"\r\n (toggleBold)=\"toggleBold()\"\r\n (toggleItalic)=\"toggleItalic()\"\r\n (toggleUnderline)=\"toggleUnderline()\"\r\n (insertLink)=\"insertLink()\"\r\n (removeFormat)=\"removeFormat()\"\r\n (saveSelectionRequest)=\"saveSelection()\"\r\n />\r\n }\r\n\r\n <div #editor\r\n class=\"stackch_rte__editor\"\r\n [attr.contenteditable]=\"!disabled\"\r\n [attr.data-placeholder]=\"placeholder || i18n.placeholder\"\r\n [style.minHeight.px]=\"minHeight\"\r\n [style.maxHeight.px]=\"maxHeight\"\r\n [style.height.px]=\"height\"\r\n (mouseup)=\"saveSelection()\"\r\n (keyup)=\"onKeyup($event)\"\r\n (input)=\"onInput()\"\r\n (blur)=\"onTouched()\"\r\n (paste)=\"onPaste($event)\"></div>\r\n</div>\r\n", styles: [".stackch_rte{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,sans-serif}.stackch_rte--disabled{opacity:.7;pointer-events:none}.stackch_rte__toolbar{display:flex;flex-wrap:wrap;gap:.25rem;align-items:center;border:1px solid #d0d7de;border-bottom:none;background:#f6f8fa;padding:.25rem;border-radius:6px 6px 0 0}.stackch_rte__btn{appearance:none;border:1px solid #d0d7de;background:#fff;padding:.25rem .5rem;border-radius:4px;cursor:pointer;transition:background .12s ease,border-color .12s ease,color .12s ease;outline:none}.stackch_rte__btn:hover{background:#f3f4f6}.stackch_rte__btn:focus{outline:none;box-shadow:none}.stackch_rte__btn:focus-visible{outline:2px solid rgba(9,105,218,.2)}.stackch_rte__btn.is-active{background:#e7f3ff;border-color:#0969da;color:#084298}.stackch_rte__select{border:1px solid #d0d7de;border-radius:4px;padding:.2rem .4rem;background:#fff}.stackch_rte__color{display:inline-flex;align-items:center;gap:.25rem;border:1px solid #d0d7de;padding:0 .4rem;border-radius:4px;background:#fff}.stackch_rte__color>input{inline-size:1.75rem;block-size:1.5rem;padding:0;border:none;background:none}.stackch_rte__sep{width:1px;height:1.25rem;background:#d0d7de;margin:0 .125rem}.stackch_rte__editor{border:1px solid #d0d7de;border-radius:0 0 6px 6px;padding:.5rem;background:#fff;overflow:auto}.stackch_rte__editor:empty:before{content:attr(data-placeholder);color:#97a1ad;pointer-events:none}.stackch_rte__editor:focus{outline:none;box-shadow:inset 0 0 0 1px #0969da;border-color:#0969da}.stackch_rte__dropdown{position:relative;display:inline-block}.stackch_rte__menu{position:absolute;top:100%;left:0;z-index:2;background:#fff;border:1px solid #d0d7de;border-radius:6px;box-shadow:0 4px 12px #00000014;padding:.25rem;min-width:220px;max-height:220px;overflow:auto}.stackch_rte__menu-item{display:block;width:100%;text-align:left;background:#fff;border:none;padding:.375rem .5rem;border-radius:4px;cursor:pointer}.stackch_rte__menu-item:hover{background:#f3f4f6}.stackch_rte__menu--row{display:flex;flex-direction:row;gap:.25rem;align-items:center;min-width:auto;max-height:none;flex-wrap:nowrap}.stackch_rte__menu--row .stackch_rte__menu-item{display:inline-flex;width:auto;text-align:center;justify-content:center;align-items:center;padding:.3rem .45rem;white-space:nowrap;flex:0 0 auto;word-break:keep-all;overflow-wrap:normal}.stackch_rte__menu--row .stackch_rte__menu-item[style*=\"width:24px\"]{padding:0;width:24px;height:24px}.stackch_rte__menu--grid{display:grid;grid-template-columns:1fr 1fr;gap:.5rem 1rem;min-width:420px}.stackch_rte__menu-section{display:flex;flex-direction:column;gap:.25rem}.stackch_rte__menu-title{font-size:12px;color:#57606a;padding:.25rem;text-transform:uppercase;letter-spacing:.04em}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "component", type: StackchRichtextEditorToolbar, selector: "stackch-richtext-editor-toolbar", inputs: ["cfg", "i18n", "disabled", "fonts", "fontSizes", "isBoldActive", "isItalicActive", "isUnderlineActive", "canUndo", "canRedo", "uiState"], outputs: ["undo", "redo", "toggleFontPanel", "toggleHeadingMenu", "toggleSpacingMenu", "toggleAlignMenu", "toggleColorMenu", "toggleListMenu", "pickFont", "pickFontSize", "pickAlign", "pickList", "pickHeading", "pickSpacing", "applyColor", "applyHighlight", "toggleBold", "toggleItalic", "toggleUnderline", "insertLink", "removeFormat", "saveSelectionRequest"] }] });
1951
+ }
1952
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImport: i0, type: StackchRichtextEditor, decorators: [{
1953
+ type: Component,
1954
+ args: [{ selector: 'stackch-richtext-editor', standalone: true, imports: [CommonModule, StackchRichtextEditorToolbar], providers: [
1955
+ {
1956
+ provide: NG_VALUE_ACCESSOR,
1957
+ useExisting: forwardRef(() => StackchRichtextEditor),
1958
+ multi: true,
1959
+ },
1960
+ ], template: "<div class=\"stackch_rte\" [class.stackch_rte--disabled]=\"disabled\">\r\n @if (showToolbar) {\r\n <stackch-richtext-editor-toolbar\r\n [cfg]=\"cfg\"\r\n [i18n]=\"i18n\"\r\n [disabled]=\"disabled\"\r\n [fonts]=\"fonts\"\r\n [fontSizes]=\"fontSizes\"\r\n [isBoldActive]=\"isBoldActive\"\r\n [isItalicActive]=\"isItalicActive\"\r\n [isUnderlineActive]=\"isUnderlineActive\"\r\n [canUndo]=\"canUndo\"\r\n [canRedo]=\"canRedo\"\r\n [uiState]=\"{ showFontPanel: showFontPanel, showHeadingMenu: showHeadingMenu, showSpacingMenu: showSpacingMenu, showAlignMenu: showAlignMenu, showColorMenu: showColorMenu, showListMenu: showListMenu }\"\r\n\r\n (undo)=\"undo()\"\r\n (redo)=\"redo()\"\r\n (toggleFontPanel)=\"toggleFontPanel($event)\"\r\n (toggleHeadingMenu)=\"toggleHeadingMenu($event)\"\r\n (toggleSpacingMenu)=\"toggleSpacingMenu($event)\"\r\n (toggleAlignMenu)=\"toggleAlignMenu($event)\"\r\n (toggleColorMenu)=\"toggleColorMenu($event)\"\r\n (toggleListMenu)=\"toggleListMenu($event)\"\r\n (pickFont)=\"onPickFont($any($event))\"\r\n (pickFontSize)=\"onPickFontSize($any($event))\"\r\n (pickAlign)=\"onPickAlign($any($event))\"\r\n (pickList)=\"onPickList($any($event))\"\r\n (pickHeading)=\"onPickHeading($any($event))\"\r\n (pickSpacing)=\"onPickSpacing($any($event).kind, $any($event).target, $any($event).value)\"\r\n (applyColor)=\"applyColor($any($event))\"\r\n (applyHighlight)=\"applyHighlight($any($event))\"\r\n (toggleBold)=\"toggleBold()\"\r\n (toggleItalic)=\"toggleItalic()\"\r\n (toggleUnderline)=\"toggleUnderline()\"\r\n (insertLink)=\"insertLink()\"\r\n (removeFormat)=\"removeFormat()\"\r\n (saveSelectionRequest)=\"saveSelection()\"\r\n />\r\n }\r\n\r\n <div #editor\r\n class=\"stackch_rte__editor\"\r\n [attr.contenteditable]=\"!disabled\"\r\n [attr.data-placeholder]=\"placeholder || i18n.placeholder\"\r\n [style.minHeight.px]=\"minHeight\"\r\n [style.maxHeight.px]=\"maxHeight\"\r\n [style.height.px]=\"height\"\r\n (mouseup)=\"saveSelection()\"\r\n (keyup)=\"onKeyup($event)\"\r\n (input)=\"onInput()\"\r\n (blur)=\"onTouched()\"\r\n (paste)=\"onPaste($event)\"></div>\r\n</div>\r\n", styles: [".stackch_rte{font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,Arial,sans-serif}.stackch_rte--disabled{opacity:.7;pointer-events:none}.stackch_rte__toolbar{display:flex;flex-wrap:wrap;gap:.25rem;align-items:center;border:1px solid #d0d7de;border-bottom:none;background:#f6f8fa;padding:.25rem;border-radius:6px 6px 0 0}.stackch_rte__btn{appearance:none;border:1px solid #d0d7de;background:#fff;padding:.25rem .5rem;border-radius:4px;cursor:pointer;transition:background .12s ease,border-color .12s ease,color .12s ease;outline:none}.stackch_rte__btn:hover{background:#f3f4f6}.stackch_rte__btn:focus{outline:none;box-shadow:none}.stackch_rte__btn:focus-visible{outline:2px solid rgba(9,105,218,.2)}.stackch_rte__btn.is-active{background:#e7f3ff;border-color:#0969da;color:#084298}.stackch_rte__select{border:1px solid #d0d7de;border-radius:4px;padding:.2rem .4rem;background:#fff}.stackch_rte__color{display:inline-flex;align-items:center;gap:.25rem;border:1px solid #d0d7de;padding:0 .4rem;border-radius:4px;background:#fff}.stackch_rte__color>input{inline-size:1.75rem;block-size:1.5rem;padding:0;border:none;background:none}.stackch_rte__sep{width:1px;height:1.25rem;background:#d0d7de;margin:0 .125rem}.stackch_rte__editor{border:1px solid #d0d7de;border-radius:0 0 6px 6px;padding:.5rem;background:#fff;overflow:auto}.stackch_rte__editor:empty:before{content:attr(data-placeholder);color:#97a1ad;pointer-events:none}.stackch_rte__editor:focus{outline:none;box-shadow:inset 0 0 0 1px #0969da;border-color:#0969da}.stackch_rte__dropdown{position:relative;display:inline-block}.stackch_rte__menu{position:absolute;top:100%;left:0;z-index:2;background:#fff;border:1px solid #d0d7de;border-radius:6px;box-shadow:0 4px 12px #00000014;padding:.25rem;min-width:220px;max-height:220px;overflow:auto}.stackch_rte__menu-item{display:block;width:100%;text-align:left;background:#fff;border:none;padding:.375rem .5rem;border-radius:4px;cursor:pointer}.stackch_rte__menu-item:hover{background:#f3f4f6}.stackch_rte__menu--row{display:flex;flex-direction:row;gap:.25rem;align-items:center;min-width:auto;max-height:none;flex-wrap:nowrap}.stackch_rte__menu--row .stackch_rte__menu-item{display:inline-flex;width:auto;text-align:center;justify-content:center;align-items:center;padding:.3rem .45rem;white-space:nowrap;flex:0 0 auto;word-break:keep-all;overflow-wrap:normal}.stackch_rte__menu--row .stackch_rte__menu-item[style*=\"width:24px\"]{padding:0;width:24px;height:24px}.stackch_rte__menu--grid{display:grid;grid-template-columns:1fr 1fr;gap:.5rem 1rem;min-width:420px}.stackch_rte__menu-section{display:flex;flex-direction:column;gap:.25rem}.stackch_rte__menu-title{font-size:12px;color:#57606a;padding:.25rem;text-transform:uppercase;letter-spacing:.04em}\n"] }]
1961
+ }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }] });
1962
+
2003
1963
  /*
2004
1964
  * Public API Surface of richtext-editor
2005
1965
  */
@@ -2008,5 +1968,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.4", ngImpor
2008
1968
  * Generated bundle index. Do not edit.
2009
1969
  */
2010
1970
 
2011
- export { STACKCH_RTE_I18N_DE, STACKCH_RTE_I18N_FR, STACKCH_RTE_I18N_IT, StackchRichtextEditor, StackchRichtextEditorConfig, StackchRichtextEditorI18n, StackchRichtextEditorToolbar };
1971
+ export { STACKCH_RTE_I18N_DE, STACKCH_RTE_I18N_FR, STACKCH_RTE_I18N_IT, StackchRichtextEditor, StackchRichtextEditorBase, StackchRichtextEditorConfig, StackchRichtextEditorI18n, StackchRichtextEditorToolbar };
2012
1972
  //# sourceMappingURL=stackch-angular-richtext-editor.mjs.map