@sd-angular/core 19.0.0-beta.2 → 19.0.0-beta.4

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 (56) hide show
  1. package/components/document-builder/src/document-builder.config.d.ts +21 -0
  2. package/components/document-builder/src/document-builder.utils.d.ts +10 -0
  3. package/fesm2022/sd-angular-core-components-document-builder.mjs +426 -154
  4. package/fesm2022/sd-angular-core-components-document-builder.mjs.map +1 -1
  5. package/fesm2022/sd-angular-core-components-table.mjs +5 -5
  6. package/fesm2022/sd-angular-core-components-table.mjs.map +1 -1
  7. package/fesm2022/sd-angular-core-components-workflow.mjs +23 -23
  8. package/fesm2022/sd-angular-core-components-workflow.mjs.map +1 -1
  9. package/fesm2022/sd-angular-core-forms-autocomplete.mjs +22 -2
  10. package/fesm2022/sd-angular-core-forms-autocomplete.mjs.map +1 -1
  11. package/fesm2022/sd-angular-core-forms-date.mjs +13 -3
  12. package/fesm2022/sd-angular-core-forms-date.mjs.map +1 -1
  13. package/fesm2022/sd-angular-core-forms-input-number.mjs +17 -3
  14. package/fesm2022/sd-angular-core-forms-input-number.mjs.map +1 -1
  15. package/fesm2022/sd-angular-core-forms-input.mjs +18 -6
  16. package/fesm2022/sd-angular-core-forms-input.mjs.map +1 -1
  17. package/fesm2022/sd-angular-core-forms-radio.mjs +15 -2
  18. package/fesm2022/sd-angular-core-forms-radio.mjs.map +1 -1
  19. package/fesm2022/sd-angular-core-forms-select.mjs +13 -2
  20. package/fesm2022/sd-angular-core-forms-select.mjs.map +1 -1
  21. package/fesm2022/sd-angular-core-forms-textarea.mjs +9 -2
  22. package/fesm2022/sd-angular-core-forms-textarea.mjs.map +1 -1
  23. package/fesm2022/sd-angular-core-modules-auth.mjs +137 -0
  24. package/fesm2022/sd-angular-core-modules-auth.mjs.map +1 -0
  25. package/fesm2022/sd-angular-core-modules-layout.mjs +1 -1
  26. package/fesm2022/sd-angular-core-modules-layout.mjs.map +1 -1
  27. package/fesm2022/sd-angular-core-modules.mjs +1 -0
  28. package/fesm2022/sd-angular-core-modules.mjs.map +1 -1
  29. package/fesm2022/sd-angular-core-services-confirm.mjs +5 -8
  30. package/fesm2022/sd-angular-core-services-confirm.mjs.map +1 -1
  31. package/fesm2022/sd-angular-core-utilities-extensions.mjs +66 -1
  32. package/fesm2022/sd-angular-core-utilities-extensions.mjs.map +1 -1
  33. package/fesm2022/sd-angular-core-utilities-models.mjs +12 -3
  34. package/fesm2022/sd-angular-core-utilities-models.mjs.map +1 -1
  35. package/forms/autocomplete/src/autocomplete.component.d.ts +5 -1
  36. package/forms/date/src/date.component.d.ts +4 -1
  37. package/forms/input/src/input.component.d.ts +6 -4
  38. package/forms/input-number/src/input-number.component.d.ts +4 -1
  39. package/forms/radio/src/radio.component.d.ts +5 -1
  40. package/forms/select/src/select.component.d.ts +5 -1
  41. package/forms/textarea/src/textarea.component.d.ts +3 -1
  42. package/modules/auth/configurations/auth.configuration.d.ts +19 -0
  43. package/modules/auth/configurations/index.d.ts +1 -0
  44. package/modules/auth/guards/auth.guard.d.ts +11 -0
  45. package/modules/auth/guards/index.d.ts +2 -0
  46. package/modules/auth/guards/portal.guard.d.ts +11 -0
  47. package/modules/auth/index.d.ts +3 -0
  48. package/modules/auth/services/auth.model.d.ts +8 -0
  49. package/modules/auth/services/auth.service.d.ts +17 -0
  50. package/modules/auth/services/index.d.ts +2 -0
  51. package/modules/index.d.ts +1 -0
  52. package/package.json +78 -74
  53. package/utilities/extensions/index.d.ts +1 -0
  54. package/utilities/extensions/src/color.extension.d.ts +20 -0
  55. package/utilities/models/src/maybe-async.model.d.ts +1 -0
  56. package/utilities/models/src/pattern.model.d.ts +2 -2
@@ -4,9 +4,8 @@ import { CommonModule } from '@angular/common';
4
4
  import * as i1 from '@ckeditor/ckeditor5-angular';
5
5
  import { CKEditorModule } from '@ckeditor/ckeditor5-angular';
6
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';
7
- import { SdResolveMaybeAsync } from '@sd-angular/core/utilities';
8
- import { SdUtilities } from '@sd-angular/core/utilities/extensions';
9
7
  import { Subscription, Subject, throttleTime } from 'rxjs';
8
+ import { SdResolveMaybeAsync, hslToHex, rgbToHex, SdUtilities } from '@sd-angular/core/utilities';
10
9
 
11
10
  class PageNumberPlugin extends Plugin {
12
11
  init() {
@@ -208,10 +207,15 @@ class CommentPlugin extends Plugin {
208
207
  const editor = this.editor;
209
208
  // --- 1. CONVERSION: MODEL MARKER -> VIEW CSS ---
210
209
  // Biến Marker thành Highlight màu vàng
211
- editor.conversion.for('editingDowncast').markerToHighlight({
212
- model: 'comment', // Khớp với prefix của markerId (ví dụ: comment:12345)
213
- view: {
214
- classes: 'ck-comment-marker', // Class CSS sẽ được gắn vào thẻ <span> bao quanh text
210
+ editor.conversion.for('downcast').markerToHighlight({
211
+ model: 'comment',
212
+ view: data => {
213
+ return {
214
+ classes: 'ck-comment-marker',
215
+ attributes: {
216
+ 'data-comment-id': data.markerName,
217
+ },
218
+ };
215
219
  },
216
220
  });
217
221
  // --- 3. ĐĂNG KÝ UI COMPONENT: 'addCommentBtn' ---
@@ -263,6 +267,29 @@ class CommentPlugin extends Plugin {
263
267
  });
264
268
  return view;
265
269
  });
270
+ // 8. Xử lý sự kiện Copy (Clipboard Output)
271
+ this.listenTo(editor.editing.view.document, 'clipboardOutput', (evt, data) => {
272
+ const content = data.content;
273
+ editor.editing.view.change(writer => {
274
+ // Tạo range bao quanh toàn bộ nội dung clipboard
275
+ const range = writer.createRangeIn(content);
276
+ // Mảng chứa các item cần xử lý
277
+ const itemsToClean = [];
278
+ // 1. Duyệt qua để tìm các thẻ có class ck-comment-marker
279
+ for (const item of range.getItems()) {
280
+ if (item.is('element') && item.hasClass('ck-comment-marker')) {
281
+ itemsToClean.push(item);
282
+ }
283
+ }
284
+ // 2. Thực hiện xóa Class và Attribute
285
+ for (const item of itemsToClean) {
286
+ // Xóa class 'ck-comment-marker'
287
+ writer.removeClass('ck-comment-marker', item);
288
+ // Xóa thuộc tính 'data-comment-id'
289
+ writer.removeAttribute('data-comment-id', item);
290
+ }
291
+ });
292
+ });
266
293
  }
267
294
  }
268
295
 
@@ -279,6 +306,9 @@ class VariablePlugin extends Plugin {
279
306
  // 1. Định nghĩa Schema (Model)
280
307
  schema.register('variable', {
281
308
  inheritAllFrom: '$inlineObject',
309
+ allowWhere: '$text',
310
+ isInline: true,
311
+ isObject: true,
282
312
  allowAttributes: ['id', 'value', 'display', 'data'],
283
313
  });
284
314
  // 2. Conversion: Model -> View (Hiển thị ra HTML)
@@ -310,12 +340,6 @@ class VariablePlugin extends Plugin {
310
340
  classes: 'variable-widget',
311
341
  },
312
342
  model: (viewElement, { writer: modelWriter }) => {
313
- // Lấy text bên trong làm label, bỏ dấu {{ }}
314
- // const textNode = viewElement.getChild(0);
315
- // let label = '';
316
- // if (textNode && textNode.is('$text')) {
317
- // label = textNode.data.replace(/{{|}}/g, '');
318
- // }
319
343
  const dataJson = viewElement.getAttribute('data-json');
320
344
  let parsedData = null;
321
345
  try {
@@ -399,30 +423,122 @@ class VariablePlugin extends Plugin {
399
423
  });
400
424
  }
401
425
  });
402
- // this.listenTo(editingView.document, 'drop', (evt, data) => {
403
- // const config = editor.config as Config<SdEditorConfig>;
404
- // const getOption = config.get('getOption') as SdEditorConfig['getOption'];
405
- // const option = getOption?.();
406
- // // Dữ liệu kéo thả
407
- // const dataTransfer = (data as any).dataTransfer;
408
- // const jsonData = dataTransfer.getData('ck-variable');
409
- // if (!jsonData) return;
410
- // evt.stop();
411
- // const variable: SdDocumentBuilderVariable = JSON.parse(jsonData);
412
- // // Gọi callback ra ngoài Angular (nếu có)
413
- // if (option && option.onDropVariable) {
414
- // const allow = option.onDropVariable(variable, 0);
415
- // if (allow === false) return; // Angular chặn drop
416
- // }
417
- // // Insert vào Model
418
- // editor.model.change(writer => {
419
- // const variableElem = writer.createElement('variable', {
420
- // id: variable.id,
421
- // label: variable.label
422
- // });
423
- // editor.model.insertContent(variableElem);
424
- // });
425
- // });
426
+ // 5. Lắng nghe sự kiện bàn phím
427
+ let isNavigating = false;
428
+ this.editor.editing.view.document.on('keydown', (evt, data) => {
429
+ // phím mũi tên: 37 (Left), 38 (Up), 39 (Right), 40 (Down)
430
+ const isArrowKey = data.keyCode >= 37 && data.keyCode <= 40;
431
+ if (isArrowKey) {
432
+ isNavigating = true;
433
+ }
434
+ else {
435
+ isNavigating = false;
436
+ }
437
+ }, { priority: 'high' });
438
+ // 6. Lắng nghe sự kiện Click chuột
439
+ this.editor.editing.view.document.on('mousedown', () => {
440
+ isNavigating = true;
441
+ });
442
+ this.listenTo(editor.model.document.selection, 'change:range', () => {
443
+ // Nếu không phải là hành động click hoặc mũi tên thì thoát hàm.
444
+ if (!isNavigating) {
445
+ return;
446
+ }
447
+ const model = editor.model;
448
+ const selection = model.document.selection;
449
+ if (!selection.isCollapsed)
450
+ return;
451
+ const position = selection.getFirstPosition();
452
+ const nodeBefore = position?.nodeBefore;
453
+ if (!position)
454
+ return;
455
+ // Kiểm tra: Node đứng trước con trỏ là variable
456
+ if (nodeBefore && nodeBefore.is('element', 'variable')) {
457
+ // Lấy node ngay sau variable để kiểm tra
458
+ const nextNode = nodeBefore.nextSibling;
459
+ // Logic: Nếu phía sau KHÔNG CÓ GÌ hoặc KHÔNG PHẢI LÀ TEXT
460
+ if (!nextNode || !nextNode.is('$text')) {
461
+ model.change(writer => {
462
+ // Chèn thêm con trỏ variable
463
+ writer.insertText('\u00A0', nodeBefore, 'after');
464
+ // Lấy vị trí ngay sau variable (lúc này đang là đầu của text node mới)
465
+ const posAfterVariable = writer.createPositionAfter(nodeBefore);
466
+ // Dịch chuyển vị trí đó sang phải 1 đơn vị (bỏ qua ký tự vừa thêm)
467
+ const targetPos = posAfterVariable.getShiftedBy(1);
468
+ // 3. Đặt con trỏ vào vị trí đã tính toán
469
+ writer.setSelection(targetPos);
470
+ });
471
+ }
472
+ }
473
+ });
474
+ // 7. Handle xóa variable
475
+ this.editor.editing.view.document.on('keydown', (evt, data) => {
476
+ // Mã phím 8 là Backspace, 46 là Delete
477
+ const btnBackspace = data.keyCode === 8;
478
+ const btnDelete = data.keyCode === 46;
479
+ if (btnBackspace || btnDelete) {
480
+ const selection = editor.model.document.selection;
481
+ const model = editor.model;
482
+ // CASE 1: Nếu con trỏ đang nhấp nháy (Collapsed)
483
+ if (selection.isCollapsed) {
484
+ const position = selection.getFirstPosition();
485
+ // Với Backspace ta kiểm tra nodeBefore, với Delete ta kiểm tra nodeAfter
486
+ const targetNode = data.keyCode === 8 ? position?.nodeBefore : position?.nodeAfter;
487
+ if (targetNode && targetNode.is('element', 'variable')) {
488
+ data.preventDefault();
489
+ evt.stop();
490
+ model.change(writer => {
491
+ // Chọn bao quanh Variable đó
492
+ writer.setSelection(targetNode, 'on');
493
+ });
494
+ return;
495
+ }
496
+ }
497
+ // CASE 2: Nếu đang có một vùng chọn (đã được highlight từ lần bấm trước)
498
+ else {
499
+ const selectedElement = selection.getSelectedElement();
500
+ // Nếu phần tử đang được chọn chính là variable
501
+ if (selectedElement && selectedElement.is('element', 'variable')) {
502
+ // Cho phép hành động mặc định diễn ra (CKEditor sẽ tự xóa phần tử đang được chọn)
503
+ // Hoặc chủ động xóa để chắc chắn:
504
+ data.preventDefault();
505
+ evt.stop();
506
+ model.change(writer => {
507
+ writer.remove(selectedElement);
508
+ });
509
+ }
510
+ }
511
+ }
512
+ }, { priority: 'highest' });
513
+ // 8. Xử lý sự kiện Copy (Clipboard Output)
514
+ // Khi copy, thay thế variable bằng text
515
+ this.listenTo(editor.editing.view.document, 'clipboardOutput', (evt, data) => {
516
+ const content = data.content;
517
+ editor.editing.view.change(writer => {
518
+ // Tạo range bao quanh toàn bộ nội dung clipboard
519
+ const range = writer.createRangeIn(content);
520
+ const itemsToReplace = [];
521
+ // Duyệt qua tất cả các phần tử trong clipboard để tìm variable
522
+ for (const item of range.getItems()) {
523
+ // Kiểm tra đúng là thẻ span và có class variable-widget
524
+ if (item.is('element', 'span') && item.hasClass('variable-widget')) {
525
+ itemsToReplace.push(item);
526
+ }
527
+ }
528
+ // Thay thế variable bằng text
529
+ for (const item of itemsToReplace) {
530
+ const displayText = item.getAttribute('data-display');
531
+ if (displayText) {
532
+ // Tạo một node text thuần túy
533
+ const textNode = writer.createText(`{{${displayText}}}`);
534
+ // Chèn text node vào ngay trước widget cũ
535
+ writer.insert(writer.createPositionBefore(item), textNode);
536
+ // Xóa widget cũ đi
537
+ writer.remove(item);
538
+ }
539
+ }
540
+ });
541
+ });
426
542
  }
427
543
  #isSdDocumentBuilderVariableResult = (obj) => {
428
544
  return (obj !== null &&
@@ -537,8 +653,203 @@ class Base64UploadAdapter {
537
653
  }
538
654
  }
539
655
 
656
+ /**
657
+ * Cấu hình màu cho Document Builder
658
+ * Bảng màu tập trung và cấu hình cho việc lựa chọn màu nhất quán
659
+ */
660
+ /**
661
+ * Trả về bảng màu chung được sử dụng trong tất cả tính năng của document builder
662
+ * @returns Mảng các tùy chọn màu được định sẵn với giá trị hex và label
663
+ */
664
+ function getPresetColors() {
665
+ return [
666
+ { color: '#000000', label: 'Black' },
667
+ { color: '#4D4D4D', label: 'Dim grey' },
668
+ { color: '#999999', label: 'Grey' },
669
+ { color: '#E6E6E6', label: 'Light grey' },
670
+ { color: '#FFFFFF', label: 'White' },
671
+ { color: '#E64D4D', label: 'Red' },
672
+ { color: '#E6994D', label: 'Orange' },
673
+ { color: '#E6E64D', label: 'Yellow' },
674
+ { color: '#99E64D', label: 'Light green' },
675
+ { color: '#4DE64D', label: 'Green' },
676
+ { color: '#4DE699', label: 'Aquamarine' },
677
+ { color: '#4DE6E6', label: 'Turquoise' },
678
+ { color: '#4D99E6', label: 'Light blue' },
679
+ { color: '#4D4DE6', label: 'Blue' },
680
+ { color: '#994DE6', label: 'Purple' },
681
+ ];
682
+ }
683
+ /**
684
+ * Trả về cấu hình bộ chọn màu với định dạng hex
685
+ * @returns Đối tượng cấu hình bộ chọn màu
686
+ */
687
+ function getColorPickerConfig() {
688
+ return {
689
+ format: 'hex',
690
+ };
691
+ }
692
+ /**
693
+ * Trả về cấu hình kích thước font cho document builder
694
+ * @returns Mảng các tùy chọn kích thước font được định sẵn
695
+ */
696
+ function getFontSizeOptions() {
697
+ return [
698
+ {
699
+ title: '9',
700
+ model: '9pt',
701
+ view: {
702
+ name: 'span',
703
+ styles: { 'font-size': '9pt' },
704
+ priority: 7,
705
+ },
706
+ },
707
+ {
708
+ title: '10',
709
+ model: '10pt',
710
+ view: {
711
+ name: 'span',
712
+ styles: { 'font-size': '10pt' },
713
+ priority: 7,
714
+ },
715
+ },
716
+ {
717
+ title: '11',
718
+ model: '11pt',
719
+ view: {
720
+ name: 'span',
721
+ styles: { 'font-size': '11pt' },
722
+ priority: 7,
723
+ },
724
+ },
725
+ {
726
+ title: '12',
727
+ model: '12pt',
728
+ view: {
729
+ name: 'span',
730
+ styles: { 'font-size': '12pt' },
731
+ priority: 7,
732
+ },
733
+ },
734
+ {
735
+ title: '13',
736
+ model: '13pt',
737
+ view: {
738
+ name: 'span',
739
+ styles: { 'font-size': '13pt' },
740
+ priority: 7,
741
+ },
742
+ },
743
+ {
744
+ title: '14',
745
+ model: '14pt',
746
+ view: {
747
+ name: 'span',
748
+ styles: { 'font-size': '14pt' },
749
+ priority: 7,
750
+ },
751
+ },
752
+ {
753
+ title: '16',
754
+ model: '16pt',
755
+ view: {
756
+ name: 'span',
757
+ styles: { 'font-size': '16pt' },
758
+ priority: 7,
759
+ },
760
+ },
761
+ {
762
+ title: '18',
763
+ model: '18pt',
764
+ view: {
765
+ name: 'span',
766
+ styles: { 'font-size': '18pt' },
767
+ priority: 7,
768
+ },
769
+ },
770
+ {
771
+ title: '20',
772
+ model: '20pt',
773
+ view: {
774
+ name: 'span',
775
+ styles: { 'font-size': '20pt' },
776
+ priority: 7,
777
+ },
778
+ },
779
+ {
780
+ title: '24',
781
+ model: '24pt',
782
+ view: {
783
+ name: 'span',
784
+ styles: { 'font-size': '24pt' },
785
+ priority: 7,
786
+ },
787
+ },
788
+ ];
789
+ }
790
+ function getHeadingOptions() {
791
+ return [
792
+ { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
793
+ { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
794
+ { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
795
+ { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
796
+ ];
797
+ }
798
+
799
+ /**
800
+ * Document Builder Utilities
801
+ * Các hàm tiện ích cho document builder
802
+ */
803
+ /**
804
+ * Chuẩn hóa nội dung bằng cách chuyển đổi tất cả màu HSL và RGB sang hex
805
+ * @param content - Nội dung HTML cần chuẩn hóa
806
+ * @returns Nội dung đã được chuẩn hóa với màu hex
807
+ */
808
+ function normalize(content) {
809
+ let normalized = content;
810
+ // Chuyển đổi HSL sang hex
811
+ const hslRegex = /hsl\(\s*(\d+)\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*\)/gi;
812
+ normalized = normalized.replace(hslRegex, (match, h, s, l) => {
813
+ try {
814
+ const hue = parseInt(h, 10);
815
+ const saturation = parseInt(s, 10);
816
+ const lightness = parseInt(l, 10);
817
+ // Kiểm tra giá trị hợp lệ
818
+ if (hue >= 0 && hue <= 360 && saturation >= 0 && saturation <= 100 && lightness >= 0 && lightness <= 100) {
819
+ return hslToHex(hue, saturation, lightness);
820
+ }
821
+ }
822
+ catch (error) {
823
+ console.warn('Failed to convert HSL to hex:', error, match);
824
+ }
825
+ return match; // Giữ nguyên nếu không thể chuyển đổi
826
+ });
827
+ // Chuyển đổi RGB sang hex
828
+ const rgbRegex = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/gi;
829
+ normalized = normalized.replace(rgbRegex, (match, r, g, b) => {
830
+ try {
831
+ const red = parseInt(r, 10);
832
+ const green = parseInt(g, 10);
833
+ const blue = parseInt(b, 10);
834
+ if (red >= 0 && red <= 255 && green >= 0 && green <= 255 && blue >= 0 && blue <= 255) {
835
+ return rgbToHex(red, green, blue);
836
+ }
837
+ }
838
+ catch (error) {
839
+ console.warn('Failed to convert RGB to hex:', error, match);
840
+ }
841
+ return match;
842
+ });
843
+ return normalized;
844
+ }
845
+
540
846
  class SdDocumentBuilder {
541
847
  #id = '1212';
848
+ // Shared color palette and configuration
849
+ #sharedColors = getPresetColors();
850
+ #colorPickerConfig = getColorPickerConfig();
851
+ #fontSizeOptions = getFontSizeOptions();
852
+ #headingOptions = getHeadingOptions();
542
853
  option;
543
854
  disabled = false;
544
855
  set _disabled(val) {
@@ -547,6 +858,7 @@ class SdDocumentBuilder {
547
858
  }
548
859
  Editor = ClassicEditor;
549
860
  #editor;
861
+ #idTimeOutScrollHeading = null;
550
862
  // Config
551
863
  config = {
552
864
  getOption: () => this.option,
@@ -625,109 +937,24 @@ class SdDocumentBuilder {
625
937
  toolbar: ['toggleImageCaption', '|', 'imageStyle:inline', 'imageStyle:block', 'imageStyle:side'],
626
938
  },
627
939
  fontSize: {
628
- options: [
629
- // Định nghĩa từng size một cách tường minh
630
- {
631
- title: '9',
632
- model: '9pt',
633
- view: {
634
- name: 'span',
635
- styles: { 'font-size': '9pt' },
636
- priority: 7,
637
- },
638
- },
639
- {
640
- title: '10',
641
- model: '10pt',
642
- view: {
643
- name: 'span',
644
- styles: { 'font-size': '10pt' },
645
- priority: 7,
646
- },
647
- },
648
- {
649
- title: '11',
650
- model: '11pt',
651
- view: {
652
- name: 'span',
653
- styles: { 'font-size': '11pt' },
654
- priority: 7,
655
- },
656
- },
657
- {
658
- title: '12',
659
- model: '12pt',
660
- view: {
661
- name: 'span',
662
- styles: { 'font-size': '12pt' },
663
- priority: 7,
664
- },
665
- },
666
- {
667
- title: '13',
668
- model: '13pt',
669
- view: {
670
- name: 'span',
671
- styles: { 'font-size': '13pt' },
672
- priority: 7,
673
- },
674
- },
675
- {
676
- title: '14',
677
- model: '14pt',
678
- view: {
679
- name: 'span',
680
- styles: { 'font-size': '14pt' },
681
- priority: 7,
682
- },
683
- },
684
- {
685
- title: '16',
686
- model: '16pt',
687
- view: {
688
- name: 'span',
689
- styles: { 'font-size': '16pt' },
690
- priority: 7,
691
- },
692
- },
693
- {
694
- title: '18',
695
- model: '18pt',
696
- view: {
697
- name: 'span',
698
- styles: { 'font-size': '18pt' },
699
- priority: 7,
700
- },
701
- },
702
- {
703
- title: '20',
704
- model: '20pt',
705
- view: {
706
- name: 'span',
707
- styles: { 'font-size': '20pt' },
708
- priority: 7,
709
- },
710
- },
711
- {
712
- title: '24',
713
- model: '24pt',
714
- view: {
715
- name: 'span',
716
- styles: { 'font-size': '24pt' },
717
- priority: 7,
718
- },
719
- },
720
- ],
940
+ options: this.#fontSizeOptions,
721
941
  supportAllValues: false, // Khuyên dùng false để ép user chọn đúng size chuẩn
722
942
  },
943
+ heading: {
944
+ options: this.#headingOptions,
945
+ },
723
946
  // 4. Cấu hình bảng màu (Tùy chọn)
724
947
  fontColor: {
725
- columns: 5,
948
+ // columns: 5,
726
949
  documentColors: 10,
950
+ colorPicker: this.#colorPickerConfig,
951
+ colors: this.#sharedColors,
727
952
  },
728
953
  fontBackgroundColor: {
729
- columns: 5,
954
+ // columns: 5,
730
955
  documentColors: 10,
956
+ colorPicker: this.#colorPickerConfig,
957
+ colors: this.#sharedColors,
731
958
  },
732
959
  table: {
733
960
  contentToolbar: [
@@ -738,6 +965,14 @@ class SdDocumentBuilder {
738
965
  'tableProperties', // <--- Nút chỉnh thuộc tính bảng (Viền, Màu, Width)
739
966
  'tableCellProperties',
740
967
  ],
968
+ tableProperties: {
969
+ borderColors: this.#sharedColors,
970
+ colorPicker: this.#colorPickerConfig,
971
+ },
972
+ tableCellProperties: {
973
+ borderColors: this.#sharedColors,
974
+ colorPicker: this.#colorPickerConfig,
975
+ },
741
976
  },
742
977
  // Quan trọng: Cho phép paste style từ Word nhưng bỏ qua margin/padding
743
978
  htmlSupport: {
@@ -757,12 +992,11 @@ class SdDocumentBuilder {
757
992
  contentChange = new EventEmitter(); // Emit HTML content
758
993
  #subscription = new Subscription();
759
994
  #contentChangeSubject = new Subject();
760
- #editorChangeRxjs = new Subject();
761
995
  ngOnInit() {
762
996
  // https://onemount.atlassian.net/browse/SM-1862
763
997
  // Debounce trong rxjs không hỗ trợ leading -->
764
998
  this.#subscription.add(this.#contentChangeSubject.pipe(throttleTime(500, undefined, { leading: true, trailing: true })).subscribe(content => {
765
- this.contentChange.emit(content);
999
+ this.contentChange.emit(normalize(content));
766
1000
  }));
767
1001
  }
768
1002
  ngOnDestroy() {
@@ -770,6 +1004,12 @@ class SdDocumentBuilder {
770
1004
  }
771
1005
  onReady(editor) {
772
1006
  this.#editor = editor;
1007
+ editor.conversion.for('editingDowncast').markerToHighlight({
1008
+ model: 'highlightMarker',
1009
+ view: {
1010
+ classes: 'ck-heading-highlight',
1011
+ },
1012
+ });
773
1013
  // Setup orientation plugin callback
774
1014
  try {
775
1015
  const orientationPlugin = editor.plugins.get('PageOrientationPlugin');
@@ -803,10 +1043,30 @@ class SdDocumentBuilder {
803
1043
  if (this.disabled) {
804
1044
  // Bật chế độ chỉ đọc với ID khóa
805
1045
  this.#editor.enableReadOnlyMode(this.#id);
1046
+ // Disable page orientation button
1047
+ try {
1048
+ const orientationPlugin = this.#editor.plugins.get('PageOrientationPlugin');
1049
+ if (orientationPlugin && orientationPlugin.buttonView) {
1050
+ orientationPlugin.buttonView.isEnabled = false;
1051
+ }
1052
+ }
1053
+ catch (error) {
1054
+ console.warn('Failed to disable orientation button:', error);
1055
+ }
806
1056
  }
807
1057
  else {
808
1058
  // Tắt chế độ chỉ đọc với ID khóa tương ứng
809
1059
  this.#editor.disableReadOnlyMode(this.#id);
1060
+ // Enable page orientation button
1061
+ try {
1062
+ const orientationPlugin = this.#editor.plugins.get('PageOrientationPlugin');
1063
+ if (orientationPlugin && orientationPlugin.buttonView) {
1064
+ orientationPlugin.buttonView.isEnabled = true;
1065
+ }
1066
+ }
1067
+ catch (error) {
1068
+ console.warn('Failed to enable orientation button:', error);
1069
+ }
810
1070
  }
811
1071
  }
812
1072
  scrollToComment = (markerId) => {
@@ -981,31 +1241,43 @@ class SdDocumentBuilder {
981
1241
  scrollToHeading: (id) => {
982
1242
  if (!this.#editor)
983
1243
  return;
984
- // 1. Lấy Model Element từ kho lưu trữ
985
1244
  const modelElement = this.#headingElementsMap.get(id);
986
1245
  if (modelElement) {
987
- const editor = this.#editor;
988
- const view = editor.editing.view;
989
- // 2. Chuyển đổi Model Element -> View Element
990
- // mapper.toViewElement sẽ trả về thẻ HTML ảo (View Element) tương ứng (ví dụ thẻ <h2>)
991
- const viewElement = editor.editing.mapper.toViewElement(modelElement);
1246
+ this.#editor.model.change(writer => {
1247
+ // Xóa marker
1248
+ if (this.#idTimeOutScrollHeading) {
1249
+ clearTimeout(this.#idTimeOutScrollHeading);
1250
+ }
1251
+ const currentMarker = this.#editor.model.markers.get('highlightMarker');
1252
+ if (currentMarker) {
1253
+ writer.removeMarker(currentMarker);
1254
+ }
1255
+ // Tạo Range bao trùm highlight
1256
+ const range = writer.createRangeOn(modelElement);
1257
+ // Thêm Marker mới
1258
+ writer.addMarker('highlightMarker', {
1259
+ range: range,
1260
+ usingOperation: false,
1261
+ });
1262
+ });
1263
+ // Scroll tới vị trí đó
1264
+ const viewElement = this.#editor.editing.mapper.toViewElement(modelElement);
992
1265
  if (viewElement) {
993
- // 3. Từ View Element ảo -> Lấy ra DOM thật (HTMLElement)
994
- const domElement = view.domConverter.mapViewToDom(viewElement);
1266
+ const domElement = this.#editor.editing.view.domConverter.viewToDom(viewElement);
995
1267
  if (domElement) {
996
- // 4. Dùng hàm native của trình duyệt để cuộn
997
- domElement.scrollIntoView({
998
- behavior: 'smooth',
999
- block: 'start',
1000
- inline: 'nearest',
1001
- });
1002
- // 5. (Tùy chọn) Focus và đặt con trỏ vào đó
1003
- view.focus();
1004
- editor.model.change(writer => {
1005
- writer.setSelection(modelElement, 'on');
1006
- });
1268
+ domElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
1007
1269
  }
1008
1270
  }
1271
+ // Tự động tắt marker sau 10 giây
1272
+ this.#idTimeOutScrollHeading = setTimeout(() => {
1273
+ if (this.#editor) {
1274
+ this.#editor.model.change(writer => {
1275
+ const marker = this.#editor.model.markers.get('highlightMarker');
1276
+ if (marker)
1277
+ writer.removeMarker(marker);
1278
+ });
1279
+ }
1280
+ }, 10000);
1009
1281
  }
1010
1282
  else {
1011
1283
  console.warn(`Heading with id ${id} not found.`);
@@ -1363,11 +1635,11 @@ class SdDocumentBuilder {
1363
1635
  // });
1364
1636
  }
1365
1637
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdDocumentBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
1366
- 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;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", "@charset \"UTF-8\";:host ::ng-deep .ck-comment-marker{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;cursor:pointer;transition:background-color .2s}: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\";:host ::ng-deep .variable-widget{background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9;border-radius:4px;padding:2px 6px;font-weight:600;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;font-size:10px;cursor:default;-webkit-user-select:none;user-select:none;display:inline-block;margin:0 2px;vertical-align:middle;font-size:0}: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-clipboard-drop-target-line{display:none!important}\n", "@charset \"UTF-8\";: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", "@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"], 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"] }] });
1638
+ 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;padding-bottom:20px}:host{display:inline-block}:host ::ng-deep .ck-heading-highlight{background-color:#fef08a;animation:fadeOut 2s 8s forwards}@keyframes fadeOut{0%{background-color:#fef08a}to{background-color:transparent}}: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", "@charset \"UTF-8\";:host ::ng-deep .ck-comment-marker{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;cursor:pointer;transition:background-color .2s}: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\";: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;cursor:default;-webkit-user-select:none;user-select:none;display:inline-block;margin:0 4px;vertical-align:middle;font-size:0}: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-clipboard-drop-target-line{display:none!important}: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", "@charset \"UTF-8\";: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", "@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"], 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"] }] });
1367
1639
  }
1368
1640
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdDocumentBuilder, decorators: [{
1369
1641
  type: Component,
1370
- 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;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", "@charset \"UTF-8\";:host ::ng-deep .ck-comment-marker{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;cursor:pointer;transition:background-color .2s}: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\";:host ::ng-deep .variable-widget{background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9;border-radius:4px;padding:2px 6px;font-weight:600;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif;font-size:10px;cursor:default;-webkit-user-select:none;user-select:none;display:inline-block;margin:0 2px;vertical-align:middle;font-size:0}: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-clipboard-drop-target-line{display:none!important}\n", "@charset \"UTF-8\";: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", "@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"] }]
1642
+ 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;padding-bottom:20px}:host{display:inline-block}:host ::ng-deep .ck-heading-highlight{background-color:#fef08a;animation:fadeOut 2s 8s forwards}@keyframes fadeOut{0%{background-color:#fef08a}to{background-color:transparent}}: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", "@charset \"UTF-8\";:host ::ng-deep .ck-comment-marker{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;cursor:pointer;transition:background-color .2s}: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\";: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;cursor:default;-webkit-user-select:none;user-select:none;display:inline-block;margin:0 4px;vertical-align:middle;font-size:0}: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-clipboard-drop-target-line{display:none!important}: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", "@charset \"UTF-8\";: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", "@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"] }]
1371
1643
  }], propDecorators: { option: [{
1372
1644
  type: Input,
1373
1645
  args: [{ required: true }]