@sd-angular/core 19.0.0-beta.13 → 19.0.0-beta.14

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 (43) hide show
  1. package/assets/scss/ckeditor5.scss +59 -2
  2. package/components/document-builder/src/document-builder.component.d.ts +7 -2
  3. package/components/document-builder/src/document-builder.model.d.ts +5 -1
  4. package/components/document-builder/src/plugins/highlight-range/highlight-range.plugin.d.ts +4 -0
  5. package/components/document-builder/src/plugins/image-custom/image-custom.plugin.d.ts +31 -0
  6. package/components/document-builder/src/plugins/index.d.ts +1 -0
  7. package/components/index.d.ts +1 -0
  8. package/components/mini-editor/index.d.ts +2 -0
  9. package/components/mini-editor/src/mini-editor.component.d.ts +90 -0
  10. package/components/mini-editor/src/mini-editor.model.d.ts +42 -0
  11. package/components/view/index.d.ts +1 -0
  12. package/components/view/src/view.component.d.ts +14 -0
  13. package/directives/index.d.ts +1 -0
  14. package/directives/src/sd-href.directive.d.ts +9 -0
  15. package/fesm2022/sd-angular-core-components-document-builder.mjs +464 -24
  16. package/fesm2022/sd-angular-core-components-document-builder.mjs.map +1 -1
  17. package/fesm2022/sd-angular-core-components-mini-editor.mjs +326 -0
  18. package/fesm2022/sd-angular-core-components-mini-editor.mjs.map +1 -0
  19. package/fesm2022/sd-angular-core-components-view.mjs +88 -0
  20. package/fesm2022/sd-angular-core-components-view.mjs.map +1 -0
  21. package/fesm2022/sd-angular-core-components-workflow.mjs +16 -26
  22. package/fesm2022/sd-angular-core-components-workflow.mjs.map +1 -1
  23. package/fesm2022/sd-angular-core-components.mjs +1 -0
  24. package/fesm2022/sd-angular-core-components.mjs.map +1 -1
  25. package/fesm2022/sd-angular-core-directives.mjs +51 -2
  26. package/fesm2022/sd-angular-core-directives.mjs.map +1 -1
  27. package/fesm2022/sd-angular-core-forms-radio.mjs +3 -2
  28. package/fesm2022/sd-angular-core-forms-radio.mjs.map +1 -1
  29. package/fesm2022/sd-angular-core-forms-select.mjs +5 -3
  30. package/fesm2022/sd-angular-core-forms-select.mjs.map +1 -1
  31. package/fesm2022/sd-angular-core-forms-textarea.mjs +2 -2
  32. package/fesm2022/sd-angular-core-forms-textarea.mjs.map +1 -1
  33. package/fesm2022/sd-angular-core-modules-layout.mjs +52 -17
  34. package/fesm2022/sd-angular-core-modules-layout.mjs.map +1 -1
  35. package/fesm2022/sd-angular-core-modules-oidc.mjs +0 -2
  36. package/fesm2022/sd-angular-core-modules-oidc.mjs.map +1 -1
  37. package/modules/layout/components/sidebar-v1/components/sidebar/sidebar.component.d.ts +1 -0
  38. package/modules/layout/components/sidebar-v1/components/user/user.component.d.ts +5 -2
  39. package/modules/layout/configurations/layout.configuration.d.ts +3 -0
  40. package/modules/layout/services/storage/storage.service.d.ts +1 -0
  41. package/package.json +57 -49
  42. package/sd-angular-core-19.0.0-beta.14.tgz +0 -0
  43. package/sd-angular-core-19.0.0-beta.13.tgz +0 -0
@@ -3,7 +3,7 @@ import { EventEmitter, Output, Input, Component } from '@angular/core';
3
3
  import { CommonModule } from '@angular/common';
4
4
  import * as i1 from '@ckeditor/ckeditor5-angular';
5
5
  import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
6
- import { Plugin, ButtonView, ClassicEditor, Essentials, Paragraph, Bold, Italic, Underline, FontSize, FontColor, FontBackgroundColor, Alignment, Widget, toWidget, GeneralHtmlSupport, FontFamily, Heading, List, Table, TableToolbar, TableProperties, TableCellProperties, TableColumnResize, PasteFromOffice, PageBreak, Undo, Subscript, Superscript, Image, ImageUpload, ImageToolbar, ImageCaption, ImageResize, ImageStyle } from 'ckeditor5';
6
+ import { Plugin, ButtonView, ClassicEditor, Essentials, Paragraph, Bold, Italic, Underline, FontSize, FontColor, FontBackgroundColor, Alignment, Widget, toWidget, GeneralHtmlSupport, FontFamily, Heading, List, Table, TableToolbar, TableProperties, TableCellProperties, TableColumnResize, PasteFromOffice, PageBreak, Undo, Subscript, Superscript, Image, ImageUpload, ImageToolbar, ImageCaption, ImageResize, ImageStyle, ImageBlock, Indent, IndentBlock } from 'ckeditor5';
7
7
  import { Subscription, Subject, throttleTime } from 'rxjs';
8
8
  import { SdResolveMaybeAsync, hslToHex, rgbToHex, SdUtilities } from '@sd-angular/core/utilities';
9
9
  import { v4 } from 'uuid';
@@ -200,7 +200,7 @@ class CommentPlugin extends Plugin {
200
200
  // BẮN EVENT RA NGOÀI - KHÔNG TỰ ADD MARKER
201
201
  // Angular component sẽ xử lý logic (mở modal, validation, etc.)
202
202
  // và gọi lại hàm addComment() nếu cần
203
- option.onAddComment(range);
203
+ option.onAddComment({ range, selectedText });
204
204
  });
205
205
  return view;
206
206
  });
@@ -501,6 +501,52 @@ class VariablePlugin extends Plugin {
501
501
  class TableFitPlugin extends Plugin {
502
502
  init() {
503
503
  const editor = this.editor;
504
+ const applyTableDefaults = (writer, tableElement) => {
505
+ if (!tableElement)
506
+ return;
507
+ // Always force table width to 100% on paste/insert
508
+ writer.setAttribute('tableWidth', '100%', tableElement);
509
+ };
510
+ const applyCellBorders = (writer, tableElement) => {
511
+ if (!tableElement)
512
+ return;
513
+ for (const row of tableElement.getChildren()) {
514
+ for (const cell of row.getChildren()) {
515
+ const hasBorderColor = cell.getAttribute('tableCellBorderColor');
516
+ const hasBorderStyle = cell.getAttribute('tableCellBorderStyle');
517
+ const hasBorderWidth = cell.getAttribute('tableCellBorderWidth');
518
+ const hasPadding = cell.getAttribute('tableCellPadding');
519
+ if (!hasBorderColor) {
520
+ writer.setAttribute('tableCellBorderColor', '#000000', cell);
521
+ }
522
+ if (!hasBorderStyle) {
523
+ writer.setAttribute('tableCellBorderStyle', 'solid', cell);
524
+ }
525
+ if (!hasBorderWidth) {
526
+ writer.setAttribute('tableCellBorderWidth', '1pt', cell);
527
+ }
528
+ if (!hasPadding) {
529
+ writer.setAttribute('tableCellPadding', '0.4em', cell);
530
+ }
531
+ }
532
+ }
533
+ };
534
+ const listenAndApplyOnExecute = (commandName) => {
535
+ const cmd = editor.commands.get(commandName);
536
+ if (!cmd)
537
+ return;
538
+ this.listenTo(cmd, 'execute', () => {
539
+ editor.model.change(writer => {
540
+ const selection = editor.model.document.selection;
541
+ const position = selection.getFirstPosition();
542
+ if (!position)
543
+ return;
544
+ const tableElement = position.findAncestor('table');
545
+ applyTableDefaults(writer, tableElement);
546
+ applyCellBorders(writer, tableElement);
547
+ });
548
+ });
549
+ };
504
550
  // Can thiệp vào quá trình convert từ View (HTML Paste) sang Model
505
551
  editor.conversion.for('upcast').add(dispatcher => {
506
552
  dispatcher.on('element:table', (evt, data, conversionApi) => {
@@ -514,7 +560,8 @@ class TableFitPlugin extends Plugin {
514
560
  for (const item of modelRange.getItems()) {
515
561
  if (item.is('element', 'table')) {
516
562
  editor.model.change(writer => {
517
- writer.setAttribute('tableWidth', '100%', item);
563
+ applyTableDefaults(writer, item);
564
+ applyCellBorders(writer, item);
518
565
  });
519
566
  }
520
567
  }
@@ -524,6 +571,52 @@ class TableFitPlugin extends Plugin {
524
571
  }, { priority: 'low' });
525
572
  // Chạy sau cùng để ghi đè các logic mặc định
526
573
  });
574
+ const findInnerTable = (viewElement) => {
575
+ if (!viewElement)
576
+ return null;
577
+ if (viewElement.name === 'table')
578
+ return viewElement;
579
+ for (const child of viewElement.getChildren()) {
580
+ if (child.name === 'table')
581
+ return child;
582
+ const found = findInnerTable(child);
583
+ if (found)
584
+ return found;
585
+ }
586
+ return null;
587
+ };
588
+ editor.conversion.for('downcast').add(dispatcher => {
589
+ // Handle when tableWidth attribute changes
590
+ dispatcher.on('attribute:tableWidth:table', (evt, data, conversionApi) => {
591
+ const viewWriter = conversionApi.writer;
592
+ const viewElement = conversionApi.mapper.toViewElement(data.item);
593
+ if (!viewElement)
594
+ return;
595
+ const innerTable = findInnerTable(viewElement);
596
+ if (!innerTable)
597
+ return;
598
+ viewWriter.setStyle('border-collapse', 'collapse', innerTable);
599
+ viewWriter.setStyle('margin', '0', innerTable);
600
+ viewWriter.setStyle('width', '100%', innerTable);
601
+ // Set width on the wrapper container (ck-widget) as well
602
+ viewWriter.setStyle('width', '100%', viewElement);
603
+ });
604
+ // Handle when table is first inserted
605
+ dispatcher.on('insert:table', (evt, data, conversionApi) => {
606
+ const viewWriter = conversionApi.writer;
607
+ const viewElement = conversionApi.mapper.toViewElement(data.item);
608
+ if (!viewElement)
609
+ return;
610
+ const innerTable = findInnerTable(viewElement);
611
+ if (!innerTable)
612
+ return;
613
+ viewWriter.setStyle('border-collapse', 'collapse', innerTable);
614
+ viewWriter.setStyle('margin', '0', innerTable);
615
+ viewWriter.setStyle('width', '100%', innerTable);
616
+ // Set width on the wrapper container (ck-widget) as well
617
+ viewWriter.setStyle('width', '100%', viewElement);
618
+ });
619
+ });
527
620
  // Lắng nghe lệnh insertTable để can thiệp ngay sau khi bảng được tạo
528
621
  const insertTableCommand = editor.commands.get('insertTable');
529
622
  if (insertTableCommand) {
@@ -540,11 +633,20 @@ class TableFitPlugin extends Plugin {
540
633
  const tableElement = position.findAncestor('table');
541
634
  if (tableElement) {
542
635
  // Ép width 100% cho bảng mới vẽ
543
- writer.setAttribute('tableWidth', '100%', tableElement);
636
+ applyTableDefaults(writer, tableElement);
637
+ // Apply border style 1pt solid black
638
+ writer.setAttribute('tableBorderColor', '#000000', tableElement);
639
+ writer.setAttribute('tableBorderStyle', 'solid', tableElement);
640
+ writer.setAttribute('tableBorderWidth', '1pt', tableElement);
641
+ applyCellBorders(writer, tableElement);
544
642
  }
545
643
  });
546
644
  });
547
645
  }
646
+ listenAndApplyOnExecute('insertTableRowAbove');
647
+ listenAndApplyOnExecute('insertTableRowBelow');
648
+ listenAndApplyOnExecute('insertTableColumnLeft');
649
+ listenAndApplyOnExecute('insertTableColumnRight');
548
650
  }
549
651
  }
550
652
 
@@ -592,6 +694,243 @@ class Base64UploadAdapter {
592
694
  }
593
695
  }
594
696
 
697
+ class ImageCustomPlugin extends Plugin {
698
+ static get pluginName() {
699
+ return 'ImageCustomPlugin';
700
+ }
701
+ init() {
702
+ const editor = this.editor;
703
+ // Thiết lập style mặc định là alignCenter khi chèn ảnh
704
+ editor.commands.get('imageUpload')?.on('execute', (evt, args) => {
705
+ // Đặt style mặc định sau khi ảnh được chèn
706
+ setTimeout(() => {
707
+ const selection = editor.model.document.selection;
708
+ const imageElement = selection.getSelectedElement();
709
+ if (imageElement && (imageElement.name === 'imageBlock' || imageElement.name === 'imageInline')) {
710
+ const currentStyle = imageElement.getAttribute('imageStyle');
711
+ // Chỉ đặt mặc định nếu chưa có style nào
712
+ if (!currentStyle) {
713
+ editor.model.change(writer => {
714
+ writer.setAttribute('imageStyle', 'alignCenter', imageElement);
715
+ });
716
+ }
717
+ }
718
+ }, 0);
719
+ });
720
+ // Downcast: Model -> View (HTML output)
721
+ // CKEditor 5 có 2 loại ảnh: imageBlock và imageInline
722
+ editor.conversion.for('downcast').add(dispatcher => {
723
+ // Xử lý ảnh block (được wrap trong figure)
724
+ dispatcher.on('insert:imageBlock', (evt, data, conversionApi) => {
725
+ this.handleImageInsert(evt, data, conversionApi);
726
+ }, { priority: 'low' });
727
+ // Xử lý ảnh inline
728
+ dispatcher.on('insert:imageInline', (evt, data, conversionApi) => {
729
+ this.handleImageInsert(evt, data, conversionApi);
730
+ }, { priority: 'low' });
731
+ // Xử lý thay đổi attribute cho cả 2 loại ảnh
732
+ ['imageBlock', 'imageInline'].forEach(imageType => {
733
+ // Xử lý thay đổi src
734
+ dispatcher.on(`attribute:src:${imageType}`, (evt, data, conversionApi) => {
735
+ this.handleImageAttributeChange(evt, data, conversionApi);
736
+ }, { priority: 'low' });
737
+ // Xử lý thay đổi width
738
+ dispatcher.on(`attribute:width:${imageType}`, (evt, data, conversionApi) => {
739
+ this.handleImageAttributeChange(evt, data, conversionApi);
740
+ }, { priority: 'low' });
741
+ // Xử lý thay đổi height
742
+ dispatcher.on(`attribute:height:${imageType}`, (evt, data, conversionApi) => {
743
+ this.handleImageAttributeChange(evt, data, conversionApi);
744
+ }, { priority: 'low' });
745
+ // Xử lý thay đổi imageStyle (căn chỉnh) - thêm float inline style
746
+ dispatcher.on(`attribute:imageStyle:${imageType}`, (evt, data, conversionApi) => {
747
+ this.handleImageStyleChange(evt, data, conversionApi);
748
+ }, { priority: 'low' });
749
+ });
750
+ });
751
+ // Xử lý upcast (HTML paste) - xóa aspect-ratio từ HTML đầu vào
752
+ editor.conversion.for('upcast').add(dispatcher => {
753
+ dispatcher.on('element:img', (evt, data, conversionApi) => {
754
+ const viewItem = data.viewItem;
755
+ if (!viewItem)
756
+ return;
757
+ // Kiểm tra viewItem có các method cần thiết
758
+ if (typeof viewItem.getStyle !== 'function')
759
+ return;
760
+ // Xóa aspect-ratio từ inline styles nếu có
761
+ const hasAspectRatio = viewItem.getStyle('aspect-ratio');
762
+ if (hasAspectRatio && typeof viewItem.removeStyle === 'function') {
763
+ viewItem.removeStyle('aspect-ratio');
764
+ }
765
+ // Đặt custom styles nếu _styles map tồn tại
766
+ if (viewItem._styles && typeof viewItem._styles.set === 'function') {
767
+ viewItem._styles.set('margin', '0');
768
+ viewItem._styles.set('border', '0');
769
+ viewItem._styles.set('max-width', '100%');
770
+ viewItem._styles.set('height', 'auto');
771
+ }
772
+ }, { priority: 'high' });
773
+ });
774
+ }
775
+ /**
776
+ * Xử lý sự kiện chèn ảnh
777
+ */
778
+ handleImageInsert(evt, data, conversionApi) {
779
+ const viewWriter = conversionApi.writer;
780
+ const viewElement = conversionApi.mapper.toViewElement(data.item);
781
+ if (!viewElement)
782
+ return;
783
+ // viewElement có thể là figure (cho block) hoặc img itself (cho inline)
784
+ // Tìm element img thực tế
785
+ const imgElement = this.findImgElement(viewElement);
786
+ if (!imgElement)
787
+ return;
788
+ this.applyCustomStyles(viewWriter, imgElement);
789
+ }
790
+ /**
791
+ * Xử lý sự kiện thay đổi attribute ảnh
792
+ */
793
+ handleImageAttributeChange(evt, data, conversionApi) {
794
+ const viewWriter = conversionApi.writer;
795
+ const viewElement = conversionApi.mapper.toViewElement(data.item);
796
+ if (!viewElement)
797
+ return;
798
+ const imgElement = this.findImgElement(viewElement);
799
+ if (!imgElement)
800
+ return;
801
+ this.applyCustomStyles(viewWriter, imgElement);
802
+ }
803
+ /**
804
+ * Xử lý sự kiện thay đổi style ảnh - thêm float inline style cho căn chỉnh
805
+ */
806
+ handleImageStyleChange(evt, data, conversionApi) {
807
+ const viewWriter = conversionApi.writer;
808
+ const viewElement = conversionApi.mapper.toViewElement(data.item);
809
+ if (!viewElement)
810
+ return;
811
+ // Tìm container ck-widget (element figure)
812
+ const widgetElement = this.findWidgetElement(viewElement);
813
+ if (!widgetElement)
814
+ return;
815
+ // Lấy giá trị imageStyle (căn chỉnh)
816
+ const imageStyle = data.item.getAttribute('imageStyle');
817
+ // Xóa các style hiện có trước
818
+ viewWriter.removeStyle('float', widgetElement);
819
+ viewWriter.removeStyle('margin', widgetElement);
820
+ viewWriter.removeStyle('text-align', widgetElement);
821
+ // Áp dụng style dựa trên căn chỉnh ảnh
822
+ // Các options đã cấu hình: ['inline', 'alignLeft', 'alignRight', 'alignCenter']
823
+ switch (imageStyle) {
824
+ case 'inline':
825
+ // Ảnh inline - không style đặc biệt, chỉ flow inline
826
+ break;
827
+ case 'alignLeft':
828
+ // Float trái
829
+ viewWriter.setStyle('float', 'left', widgetElement);
830
+ viewWriter.setStyle('margin', '0 16px 16px 0', widgetElement);
831
+ break;
832
+ case 'alignRight':
833
+ // Float phải
834
+ viewWriter.setStyle('float', 'right', widgetElement);
835
+ viewWriter.setStyle('margin', '0 0 16px 16px', widgetElement);
836
+ break;
837
+ case 'alignCenter':
838
+ case 'block':
839
+ // Căn giữa
840
+ viewWriter.setStyle('text-align', 'center', widgetElement);
841
+ viewWriter.setStyle('margin', '16px auto', widgetElement);
842
+ break;
843
+ default:
844
+ // Mặc định - căn giữa
845
+ viewWriter.setStyle('text-align', 'center', widgetElement);
846
+ viewWriter.setStyle('margin', '16px auto', widgetElement);
847
+ break;
848
+ }
849
+ // Áp dụng custom styles cơ bản cho element img
850
+ const imgElement = this.findImgElement(viewElement);
851
+ if (imgElement) {
852
+ this.applyCustomStyles(viewWriter, imgElement);
853
+ }
854
+ }
855
+ /**
856
+ * Áp dụng custom styles cho element ảnh
857
+ */
858
+ applyCustomStyles(viewWriter, imgElement) {
859
+ // Xóa aspect-ratio nếu tồn tại
860
+ if (imgElement.getStyle('aspect-ratio')) {
861
+ viewWriter.removeStyle('aspect-ratio', imgElement);
862
+ }
863
+ // Áp dụng custom styles
864
+ viewWriter.setStyle('margin', '0', imgElement);
865
+ viewWriter.setStyle('border', '0', imgElement);
866
+ viewWriter.setStyle('max-width', '100%', imgElement);
867
+ viewWriter.setStyle('height', 'auto', imgElement);
868
+ }
869
+ /**
870
+ * Tìm container ck-widget (element figure)
871
+ * CKEditor wrap ảnh block trong <figure class="ck-widget"><img></figure>
872
+ */
873
+ findWidgetElement(viewElement) {
874
+ if (!viewElement)
875
+ return null;
876
+ // Nếu đây là element figure itself
877
+ if (viewElement.name === 'figure') {
878
+ return viewElement;
879
+ }
880
+ // Cho ảnh inline, trả về element itself (span wrapper)
881
+ if (viewElement.name === 'span') {
882
+ return viewElement;
883
+ }
884
+ // Tìm ngược lên tree để tìm figure/ck-widget
885
+ let current = viewElement;
886
+ while (current) {
887
+ if (current.name === 'figure' || current.name === 'span') {
888
+ return current;
889
+ }
890
+ // Di chuyển lên parent
891
+ if (current.parent) {
892
+ current = current.parent;
893
+ }
894
+ else {
895
+ break;
896
+ }
897
+ }
898
+ // Nếu không tìm thấy widget, trả về element gốc
899
+ return viewElement;
900
+ }
901
+ /**
902
+ * Tìm element img thực tế bên trong widget structure
903
+ * CKEditor wrap ảnh block trong <figure class="ck-widget"><img></figure>
904
+ */
905
+ findImgElement(viewElement) {
906
+ if (!viewElement)
907
+ return null;
908
+ // Nếu đây là element img itself
909
+ if (viewElement.name === 'img') {
910
+ return viewElement;
911
+ }
912
+ // Cho structure widget của CKEditor, tìm đệ quy
913
+ // Ảnh block: figure > span > img
914
+ // Ảnh thường được wrap trong một container
915
+ const queue = [viewElement];
916
+ while (queue.length > 0) {
917
+ const current = queue.shift();
918
+ if (!current)
919
+ continue;
920
+ if (current.name === 'img') {
921
+ return current;
922
+ }
923
+ // Thêm children vào queue
924
+ if (current.getChildren) {
925
+ for (const child of current.getChildren()) {
926
+ queue.push(child);
927
+ }
928
+ }
929
+ }
930
+ return null;
931
+ }
932
+ }
933
+
595
934
  // Icon khổ dọc (Mặc định cũ)
596
935
  const ICON_PORTRAIT = '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M14 2H6c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6V4h8v12z"/></svg>';
597
936
  // Icon khổ ngang (Mới)
@@ -855,6 +1194,18 @@ function normalize(content) {
855
1194
  return normalized;
856
1195
  }
857
1196
 
1197
+ class HighlightRangePlugin extends Plugin {
1198
+ init() {
1199
+ const editor = this.editor;
1200
+ editor.conversion.for('editingDowncast').markerToHighlight({
1201
+ model: 'highlightRange',
1202
+ view: {
1203
+ classes: 'highlight-range',
1204
+ },
1205
+ });
1206
+ }
1207
+ }
1208
+
858
1209
  class SdDocumentBuilder {
859
1210
  option;
860
1211
  disabled = false;
@@ -909,13 +1260,18 @@ class SdDocumentBuilder {
909
1260
  ImageCaption,
910
1261
  ImageResize,
911
1262
  ImageStyle,
1263
+ ImageBlock,
912
1264
  // Custom Plugin
913
1265
  HeadingPlugin,
914
1266
  CommentPlugin,
915
1267
  VariablePlugin,
916
1268
  TableFitPlugin,
917
- ImageUploadPlugin,
1269
+ Indent,
1270
+ IndentBlock,
918
1271
  PageOrientationPlugin,
1272
+ ImageUploadPlugin,
1273
+ ImageCustomPlugin,
1274
+ HighlightRangePlugin,
919
1275
  ],
920
1276
  toolbar: {
921
1277
  items: [
@@ -950,7 +1306,18 @@ class SdDocumentBuilder {
950
1306
  shouldNotGroupWhenFull: true,
951
1307
  },
952
1308
  image: {
953
- toolbar: ['toggleImageCaption', '|', 'imageStyle:inline', 'imageStyle:block', 'imageStyle:side'],
1309
+ styles: {
1310
+ options: ['inline', 'alignLeft', 'alignRight', 'alignCenter'],
1311
+ },
1312
+ toolbar: [
1313
+ 'imageStyle:inline',
1314
+ 'imageStyle:alignCenter',
1315
+ {
1316
+ name: 'imageStyle:alignDropdown',
1317
+ items: ['imageStyle:alignLeft', 'imageStyle:alignRight'],
1318
+ defaultItem: 'imageStyle:alignLeft',
1319
+ },
1320
+ ],
954
1321
  },
955
1322
  fontSize: {
956
1323
  options: this.#fontSizeOptions,
@@ -976,13 +1343,29 @@ class SdDocumentBuilder {
976
1343
  contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', '|', 'tableProperties', 'tableCellProperties'],
977
1344
  tableProperties: {
978
1345
  borderColors: this.#sharedColors,
1346
+ backgroundColors: this.#sharedColors,
979
1347
  colorPicker: this.#colorPickerConfig,
1348
+ defaultProperties: {
1349
+ borderStyle: 'solid',
1350
+ borderWidth: '1px',
1351
+ borderColor: '#ccc',
1352
+ },
980
1353
  },
981
1354
  tableCellProperties: {
982
1355
  borderColors: this.#sharedColors,
1356
+ backgroundColors: this.#sharedColors,
983
1357
  colorPicker: this.#colorPickerConfig,
1358
+ defaultProperties: {
1359
+ borderStyle: 'solid',
1360
+ borderWidth: '1px',
1361
+ borderColor: '#ccc',
1362
+ },
984
1363
  },
985
1364
  },
1365
+ indentBlock: {
1366
+ offset: 48, // Đơn vị px cho mỗi mức indent (tương đương 0.5 inch)
1367
+ unit: 'px',
1368
+ },
986
1369
  // Quan trọng: Cho phép paste style từ Word nhưng bỏ qua margin/padding
987
1370
  htmlSupport: {
988
1371
  allow: [
@@ -1034,6 +1417,27 @@ class SdDocumentBuilder {
1034
1417
  const content = editor.getData();
1035
1418
  this.#contentChangeSubject.next(content);
1036
1419
  });
1420
+ try {
1421
+ // Manual keybinding cho Tab nếu cần
1422
+ editor.keystrokes.set('Tab', (evt, cancel) => {
1423
+ const command = editor.commands.get('indentBlock');
1424
+ if (command && command.isEnabled) {
1425
+ editor.execute('indentBlock');
1426
+ cancel();
1427
+ }
1428
+ });
1429
+ // Manual keybinding cho Shift+Tab
1430
+ editor.keystrokes.set('Shift+Tab', (evt, cancel) => {
1431
+ const command = editor.commands.get('outdentBlock');
1432
+ if (command && command.isEnabled) {
1433
+ editor.execute('outdentBlock');
1434
+ cancel();
1435
+ }
1436
+ });
1437
+ }
1438
+ catch (error) {
1439
+ console.warn('Error setting up indent keybindings:', error);
1440
+ }
1037
1441
  this.#updateState();
1038
1442
  }
1039
1443
  setContent = (html) => {
@@ -1248,21 +1652,28 @@ class SdDocumentBuilder {
1248
1652
  all: () => {
1249
1653
  if (!this.#editor)
1250
1654
  return [];
1251
- const markers = this.#editor.model.markers;
1252
- const comments = [];
1253
- // Duyệt qua tất cả markers trong Model
1254
- for (const marker of markers) {
1255
- // Chỉ lấy marker do plugin comment tạo ra (prefix 'comment:')
1256
- if (marker.name.startsWith('comment:')) {
1257
- // Lấy text nằm trong vùng marker đó
1258
- const currentText = this.#getTextFromRange(marker.getRange());
1259
- comments.push({
1260
- markerId: marker.name,
1261
- selectedText: currentText,
1262
- });
1655
+ const editableElement = this.#editor.ui.view.editable.element;
1656
+ if (!editableElement)
1657
+ return [];
1658
+ const commentMarkers = editableElement.querySelectorAll('.ck-comment-marker[data-comment-id^="comment:"]') || [];
1659
+ const commentsMap = new Map();
1660
+ commentMarkers.forEach(el => {
1661
+ const markerId = el.getAttribute('data-comment-id');
1662
+ if (markerId) {
1663
+ const existing = commentsMap.get(markerId);
1664
+ const text = el.textContent || '';
1665
+ if (existing) {
1666
+ existing.selectedText += text;
1667
+ }
1668
+ else {
1669
+ commentsMap.set(markerId, {
1670
+ markerId,
1671
+ selectedText: text,
1672
+ });
1673
+ }
1263
1674
  }
1264
- }
1265
- return comments;
1675
+ });
1676
+ return Array.from(commentsMap.values());
1266
1677
  },
1267
1678
  /**
1268
1679
  * Thêm comment vào vùng text đang được chọn
@@ -1270,7 +1681,11 @@ class SdDocumentBuilder {
1270
1681
  * @param data - Dữ liệu extra data
1271
1682
  * @returns SdDocumentBuilderComment hoặc null nếu không có text được chọn
1272
1683
  */
1273
- add: (range, comment, data) => {
1684
+ add: (range, comment, args) => {
1685
+ // markerIdExternal khi truyền từ bên ngoài vào sẽ có thể là Date.now() hoặc uuidv4().
1686
+ // Miễn là đảm bảo markerIdExternal là unique.
1687
+ // Phục vụ cho case gọi API comment thành công thì mới sinh ra markerId.
1688
+ const { markerIdExternal, data } = args ?? {};
1274
1689
  if (!this.#editor)
1275
1690
  return null;
1276
1691
  const model = this.#editor.model;
@@ -1283,7 +1698,7 @@ class SdDocumentBuilder {
1283
1698
  return null;
1284
1699
  }
1285
1700
  // 4. Tạo ID unique cho marker
1286
- const markerId = `comment:${Date.now()}`;
1701
+ const markerId = markerIdExternal ? `comment:${markerIdExternal}` : `comment:${Date.now()}`;
1287
1702
  // 5. Tạo marker trong model
1288
1703
  model.change(writer => {
1289
1704
  writer.addMarker(markerId, {
@@ -1561,12 +1976,37 @@ class SdDocumentBuilder {
1561
1976
  const blob = new Blob(['\ufeff', fullHtml], { type: 'application/msword' });
1562
1977
  SdUtilities.downloadBlob(blob, fileName);
1563
1978
  }
1979
+ hightSelectRange = (range) => {
1980
+ if (!range)
1981
+ return;
1982
+ const editor = this.#editor;
1983
+ editor.model.change(writer => {
1984
+ // Xóa marker cũ (nếu có)
1985
+ if (editor.model.markers.has('highlightRange')) {
1986
+ writer.removeMarker('highlightRange');
1987
+ }
1988
+ // Tạo marker mới
1989
+ writer.addMarker('highlightRange', {
1990
+ usingOperation: false, // Không lưu vào lịch sử Undo/Redo
1991
+ affectsData: false, // Không ảnh hưởng đến data lấy ra (getData)
1992
+ range: range,
1993
+ });
1994
+ });
1995
+ };
1996
+ removeHighlightSeclectRange = () => {
1997
+ const editor = this.#editor;
1998
+ editor.model.change(writer => {
1999
+ if (editor.model.markers.has('highlightRange')) {
2000
+ writer.removeMarker('highlightRange');
2001
+ }
2002
+ });
2003
+ };
1564
2004
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdDocumentBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
1565
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: SdDocumentBuilder, isStandalone: true, selector: "sd-document-builder", inputs: { option: "option", _disabled: ["disabled", "_disabled"] }, outputs: { contentChange: "contentChange" }, ngImport: i0, template: "<div class=\"builder-container\">\n <ckeditor\n style=\"width: 100%\"\n [editor]=\"Editor\" \n [config]=\"config\" \n (ready)=\"onReady($event)\"\n [disabled]=\"disabled\">\n </ckeditor>\n</div>", styles: ["@charset \"UTF-8\";::ng-deep .ck-editor{--ck-font-size-base: 11px !important;--ck-icon-size: 16px !important;--ck-content-font-family: \"Times New Roman\", serif !important;--ck-content-font-size: 13pt;--ck-content-line-height: 1.5;--ck-spacing-small: 2px !important;--ck-spacing-standard: 4px !important;--ck-spacing-large: 8px !important}::ng-deep .ck-editor .ck-editor__top{position:sticky;top:0;z-index:100;width:100%;min-width:600px;margin-bottom:10px}::ng-deep .ck-editor .ck-editor__top .ck-sticky-panel__content{border:none!important}::ng-deep .ck-editor .ck-editor__top .ck-toolbar{background:#fff!important;box-shadow:0 4px 6px -1px #0000001a!important;padding:8px!important}::ng-deep .ck-editor .ck-editor__top .ck-toolbar .ck-toolbar__items{display:flex;justify-content:center;flex-wrap:wrap;align-items:center}::ng-deep .ck-editor .ck-toolbar{min-height:32px!important;padding:2px!important}::ng-deep .ck-editor .ck-button{padding:2px 4px!important;min-height:24px!important}::ng-deep .ck-editor .ck-dropdown__button{min-height:24px!important}::ng-deep .ck.ck-toolbar{background:#f8f9fa!important;border-bottom:1px solid #e0e0e0!important}\n", "@charset \"UTF-8\";.builder-container{background-color:#f3f4f6;height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;align-items:center;padding-bottom:20px}:host{display:inline-block}:host ::ng-deep .ck-editor{display:flex;flex-direction:column;align-items:center;width:100%}:host ::ng-deep .ck-editor .ck-editor__top,:host ::ng-deep .ck-editor .ck-editor__main{border:none!important;box-shadow:none!important}:host ::ng-deep .ck-content{background-color:#fff;width:210mm;min-height:1123px;padding:20mm!important;box-sizing:border-box!important;box-shadow:0 10px 15px -3px #0000001a}:host ::ng-deep .ck-content h1,:host ::ng-deep .ck-content h2,:host ::ng-deep .ck-content h3,:host ::ng-deep .ck-content h4,:host ::ng-deep .ck-content h5,:host ::ng-deep .ck-content h6{font-weight:400}:host ::ng-deep .ck-content.ck-focused{outline:none!important;border-color:#d1d5db!important}:host ::ng-deep .ck-content.landscape{width:297mm}:host ::ng-deep .ck-content>*{max-width:100%!important;box-sizing:border-box!important}:host ::ng-deep .ck-content img{max-width:100%!important;height:auto!important;object-fit:contain}:host ::ng-deep .ck-content p{margin-left:0!important;margin-right:0!important;margin-bottom:var(--ck-spacing-large);text-indent:0}:host ::ng-deep .ck-content ul,:host ::ng-deep .ck-content ol{padding-left:20px!important;margin-left:0!important}\n", ":host ::ng-deep .ck-heading-highlight{background-color:#fef08a}\n", ":host ::ng-deep .ck-comment-marker{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}:host ::ng-deep .ck-comment-marker:hover{background-color:#ffeb3bcc}:host ::ng-deep .ck-comment-marker.active-highlight{background-color:#ffeb3b;outline:2px dashed #f57f17}\n", "@charset \"UTF-8\";::ng-deep .ck-clipboard-drop-target-line{display:none!important}:host ::ng-deep .variable-widget{background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9!important;border-radius:4px;padding:2px 6px;font-weight:600;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;font-size:10px;-webkit-user-select:none;user-select:none;display:inline-block;margin:0 4px;vertical-align:middle;font-size:0;cursor:default}:host ::ng-deep .variable-widget:before{content:attr(data-display);font-size:10px}:host ::ng-deep .variable-widget:hover{background-color:#bbdefb;box-shadow:0 1px 2px #0000001a}:host ::ng-deep .variable-widget.ck-widget_selected{outline:2px solid #2196f3;background-color:#bbdefb}:host ::ng-deep .ck.ck-content .ck-widget,:host ::ng-deep .ck.ck-content .ck-widget:hover,:host ::ng-deep .ck.ck-content .ck-widget:focus,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected:hover{outline:none!important;box-shadow:none!important}\n", ":host ::ng-deep .ck-editor__editable .ck-widget.table{float:none!important;display:block!important;max-width:100%!important;width:100%!important;margin:0!important;clear:both}:host ::ng-deep .ck-editor__editable table{table-layout:auto!important;width:100%!important;border-collapse:collapse;margin:0!important}:host ::ng-deep .ck-editor__editable table td,:host ::ng-deep .ck-editor__editable table th{word-wrap:break-word;white-space:normal!important;padding:.4em!important}:host ::ng-deep .ck-editor__editable table td img,:host ::ng-deep .ck-editor__editable table th img{max-width:100%;height:auto}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i1.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableWatchdog", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }] });
2005
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.17", type: SdDocumentBuilder, isStandalone: true, selector: "sd-document-builder", inputs: { option: "option", _disabled: ["disabled", "_disabled"] }, outputs: { contentChange: "contentChange" }, ngImport: i0, template: "<div class=\"builder-container\">\n <ckeditor\n style=\"width: 100%\"\n [editor]=\"Editor\" \n [config]=\"config\" \n (ready)=\"onReady($event)\"\n [disabled]=\"disabled\">\n </ckeditor>\n</div>", styles: ["@charset \"UTF-8\";.builder-container{background-color:#f3f4f6;height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;align-items:center}:host{display:inline-block}:host ::ng-deep .ck-editor{display:flex;flex-direction:column;align-items:center;width:100%;--ck-content-font-family: \"Times New Roman\", serif !important;--ck-content-font-size: 13pt;--ck-content-line-height: 1.5}:host ::ng-deep .ck-editor__top,:host ::ng-deep .ck-editor__main{border:none!important;box-shadow:none!important}:host ::ng-deep .ck-editor__top{position:sticky;top:0;z-index:100;width:100%;min-width:600px;margin-bottom:10px}:host ::ng-deep .ck-editor__top .ck-sticky-panel__content{border:none!important}:host ::ng-deep .ck-editor__top .ck-toolbar{background:#fff!important;box-shadow:0 4px 6px -1px #0000001a!important;padding:8px!important}:host ::ng-deep .ck-editor__top .ck-toolbar .ck-toolbar__items{display:flex;justify-content:center;flex-wrap:wrap;align-items:center}:host ::ng-deep .ck-content{background-color:#fff;width:210mm;min-height:1123px;padding:20mm!important;box-sizing:border-box!important;box-shadow:0 10px 15px -3px #0000001a}:host ::ng-deep .ck-content h1,:host ::ng-deep .ck-content h2,:host ::ng-deep .ck-content h3,:host ::ng-deep .ck-content h4,:host ::ng-deep .ck-content h5,:host ::ng-deep .ck-content h6{font-weight:400}:host ::ng-deep .ck-content.landscape{width:297mm}:host ::ng-deep .ck-content>*{max-width:100%!important;box-sizing:border-box!important}:host ::ng-deep .ck-content p{margin-bottom:var(--ck-spacing-large);text-indent:0}:host ::ng-deep .ck-content ul,:host ::ng-deep .ck-content ol{padding-left:20px!important;margin-left:0!important}\n", ":host ::ng-deep .ck-heading-highlight{background-color:#fef08a}\n", ":host ::ng-deep .ck-comment-marker{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}:host ::ng-deep .ck-comment-marker:hover{background-color:#ffeb3bcc}:host ::ng-deep .ck-comment-marker.active-highlight{background-color:#ffeb3b;outline:2px dashed #f57f17}\n", "@charset \"UTF-8\";::ng-deep .ck-clipboard-drop-target-line{display:none!important}:host ::ng-deep .variable-widget{background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9!important;border-radius:4px;padding:2px 6px;font-weight:600;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;font-size:10px;-webkit-user-select:none;user-select:none;display:inline-block;margin:0 4px;vertical-align:middle;font-size:0;cursor:default}:host ::ng-deep .variable-widget:before{content:attr(data-display);font-size:10px}:host ::ng-deep .variable-widget:hover{background-color:#bbdefb;box-shadow:0 1px 2px #0000001a}:host ::ng-deep .variable-widget.ck-widget_selected{outline:2px solid #2196f3;background-color:#bbdefb}:host ::ng-deep .ck.ck-content .ck-widget,:host ::ng-deep .ck.ck-content .ck-widget:hover,:host ::ng-deep .ck.ck-content .ck-widget:focus,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected:hover{outline:none!important;box-shadow:none!important}\n", "", ":host ::ng-deep .highlight-range{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: CKEditorModule }, { kind: "component", type: i1.CKEditorComponent, selector: "ckeditor", inputs: ["editor", "config", "data", "tagName", "watchdog", "editorWatchdogConfig", "disableWatchdog", "disableTwoWayDataBinding", "disabled"], outputs: ["ready", "change", "blur", "focus", "error"] }] });
1566
2006
  }
1567
2007
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdDocumentBuilder, decorators: [{
1568
2008
  type: Component,
1569
- args: [{ selector: 'sd-document-builder', standalone: true, imports: [CommonModule, CKEditorModule], template: "<div class=\"builder-container\">\n <ckeditor\n style=\"width: 100%\"\n [editor]=\"Editor\" \n [config]=\"config\" \n (ready)=\"onReady($event)\"\n [disabled]=\"disabled\">\n </ckeditor>\n</div>", styles: ["@charset \"UTF-8\";::ng-deep .ck-editor{--ck-font-size-base: 11px !important;--ck-icon-size: 16px !important;--ck-content-font-family: \"Times New Roman\", serif !important;--ck-content-font-size: 13pt;--ck-content-line-height: 1.5;--ck-spacing-small: 2px !important;--ck-spacing-standard: 4px !important;--ck-spacing-large: 8px !important}::ng-deep .ck-editor .ck-editor__top{position:sticky;top:0;z-index:100;width:100%;min-width:600px;margin-bottom:10px}::ng-deep .ck-editor .ck-editor__top .ck-sticky-panel__content{border:none!important}::ng-deep .ck-editor .ck-editor__top .ck-toolbar{background:#fff!important;box-shadow:0 4px 6px -1px #0000001a!important;padding:8px!important}::ng-deep .ck-editor .ck-editor__top .ck-toolbar .ck-toolbar__items{display:flex;justify-content:center;flex-wrap:wrap;align-items:center}::ng-deep .ck-editor .ck-toolbar{min-height:32px!important;padding:2px!important}::ng-deep .ck-editor .ck-button{padding:2px 4px!important;min-height:24px!important}::ng-deep .ck-editor .ck-dropdown__button{min-height:24px!important}::ng-deep .ck.ck-toolbar{background:#f8f9fa!important;border-bottom:1px solid #e0e0e0!important}\n", "@charset \"UTF-8\";.builder-container{background-color:#f3f4f6;height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;align-items:center;padding-bottom:20px}:host{display:inline-block}:host ::ng-deep .ck-editor{display:flex;flex-direction:column;align-items:center;width:100%}:host ::ng-deep .ck-editor .ck-editor__top,:host ::ng-deep .ck-editor .ck-editor__main{border:none!important;box-shadow:none!important}:host ::ng-deep .ck-content{background-color:#fff;width:210mm;min-height:1123px;padding:20mm!important;box-sizing:border-box!important;box-shadow:0 10px 15px -3px #0000001a}:host ::ng-deep .ck-content h1,:host ::ng-deep .ck-content h2,:host ::ng-deep .ck-content h3,:host ::ng-deep .ck-content h4,:host ::ng-deep .ck-content h5,:host ::ng-deep .ck-content h6{font-weight:400}:host ::ng-deep .ck-content.ck-focused{outline:none!important;border-color:#d1d5db!important}:host ::ng-deep .ck-content.landscape{width:297mm}:host ::ng-deep .ck-content>*{max-width:100%!important;box-sizing:border-box!important}:host ::ng-deep .ck-content img{max-width:100%!important;height:auto!important;object-fit:contain}:host ::ng-deep .ck-content p{margin-left:0!important;margin-right:0!important;margin-bottom:var(--ck-spacing-large);text-indent:0}:host ::ng-deep .ck-content ul,:host ::ng-deep .ck-content ol{padding-left:20px!important;margin-left:0!important}\n", ":host ::ng-deep .ck-heading-highlight{background-color:#fef08a}\n", ":host ::ng-deep .ck-comment-marker{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}:host ::ng-deep .ck-comment-marker:hover{background-color:#ffeb3bcc}:host ::ng-deep .ck-comment-marker.active-highlight{background-color:#ffeb3b;outline:2px dashed #f57f17}\n", "@charset \"UTF-8\";::ng-deep .ck-clipboard-drop-target-line{display:none!important}:host ::ng-deep .variable-widget{background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9!important;border-radius:4px;padding:2px 6px;font-weight:600;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;font-size:10px;-webkit-user-select:none;user-select:none;display:inline-block;margin:0 4px;vertical-align:middle;font-size:0;cursor:default}:host ::ng-deep .variable-widget:before{content:attr(data-display);font-size:10px}:host ::ng-deep .variable-widget:hover{background-color:#bbdefb;box-shadow:0 1px 2px #0000001a}:host ::ng-deep .variable-widget.ck-widget_selected{outline:2px solid #2196f3;background-color:#bbdefb}:host ::ng-deep .ck.ck-content .ck-widget,:host ::ng-deep .ck.ck-content .ck-widget:hover,:host ::ng-deep .ck.ck-content .ck-widget:focus,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected:hover{outline:none!important;box-shadow:none!important}\n", ":host ::ng-deep .ck-editor__editable .ck-widget.table{float:none!important;display:block!important;max-width:100%!important;width:100%!important;margin:0!important;clear:both}:host ::ng-deep .ck-editor__editable table{table-layout:auto!important;width:100%!important;border-collapse:collapse;margin:0!important}:host ::ng-deep .ck-editor__editable table td,:host ::ng-deep .ck-editor__editable table th{word-wrap:break-word;white-space:normal!important;padding:.4em!important}:host ::ng-deep .ck-editor__editable table td img,:host ::ng-deep .ck-editor__editable table th img{max-width:100%;height:auto}\n"] }]
2009
+ args: [{ selector: 'sd-document-builder', standalone: true, imports: [CommonModule, CKEditorModule], template: "<div class=\"builder-container\">\n <ckeditor\n style=\"width: 100%\"\n [editor]=\"Editor\" \n [config]=\"config\" \n (ready)=\"onReady($event)\"\n [disabled]=\"disabled\">\n </ckeditor>\n</div>", styles: ["@charset \"UTF-8\";.builder-container{background-color:#f3f4f6;height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;align-items:center}:host{display:inline-block}:host ::ng-deep .ck-editor{display:flex;flex-direction:column;align-items:center;width:100%;--ck-content-font-family: \"Times New Roman\", serif !important;--ck-content-font-size: 13pt;--ck-content-line-height: 1.5}:host ::ng-deep .ck-editor__top,:host ::ng-deep .ck-editor__main{border:none!important;box-shadow:none!important}:host ::ng-deep .ck-editor__top{position:sticky;top:0;z-index:100;width:100%;min-width:600px;margin-bottom:10px}:host ::ng-deep .ck-editor__top .ck-sticky-panel__content{border:none!important}:host ::ng-deep .ck-editor__top .ck-toolbar{background:#fff!important;box-shadow:0 4px 6px -1px #0000001a!important;padding:8px!important}:host ::ng-deep .ck-editor__top .ck-toolbar .ck-toolbar__items{display:flex;justify-content:center;flex-wrap:wrap;align-items:center}:host ::ng-deep .ck-content{background-color:#fff;width:210mm;min-height:1123px;padding:20mm!important;box-sizing:border-box!important;box-shadow:0 10px 15px -3px #0000001a}:host ::ng-deep .ck-content h1,:host ::ng-deep .ck-content h2,:host ::ng-deep .ck-content h3,:host ::ng-deep .ck-content h4,:host ::ng-deep .ck-content h5,:host ::ng-deep .ck-content h6{font-weight:400}:host ::ng-deep .ck-content.landscape{width:297mm}:host ::ng-deep .ck-content>*{max-width:100%!important;box-sizing:border-box!important}:host ::ng-deep .ck-content p{margin-bottom:var(--ck-spacing-large);text-indent:0}:host ::ng-deep .ck-content ul,:host ::ng-deep .ck-content ol{padding-left:20px!important;margin-left:0!important}\n", ":host ::ng-deep .ck-heading-highlight{background-color:#fef08a}\n", ":host ::ng-deep .ck-comment-marker{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}:host ::ng-deep .ck-comment-marker:hover{background-color:#ffeb3bcc}:host ::ng-deep .ck-comment-marker.active-highlight{background-color:#ffeb3b;outline:2px dashed #f57f17}\n", "@charset \"UTF-8\";::ng-deep .ck-clipboard-drop-target-line{display:none!important}:host ::ng-deep .variable-widget{background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9!important;border-radius:4px;padding:2px 6px;font-weight:600;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;font-size:10px;-webkit-user-select:none;user-select:none;display:inline-block;margin:0 4px;vertical-align:middle;font-size:0;cursor:default}:host ::ng-deep .variable-widget:before{content:attr(data-display);font-size:10px}:host ::ng-deep .variable-widget:hover{background-color:#bbdefb;box-shadow:0 1px 2px #0000001a}:host ::ng-deep .variable-widget.ck-widget_selected{outline:2px solid #2196f3;background-color:#bbdefb}:host ::ng-deep .ck.ck-content .ck-widget,:host ::ng-deep .ck.ck-content .ck-widget:hover,:host ::ng-deep .ck.ck-content .ck-widget:focus,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected:hover{outline:none!important;box-shadow:none!important}\n", ":host ::ng-deep .highlight-range{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}\n"] }]
1570
2010
  }], propDecorators: { option: [{
1571
2011
  type: Input,
1572
2012
  args: [{ required: true }]