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

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 (39) hide show
  1. package/components/document-builder/src/document-builder.component.d.ts +7 -6
  2. package/components/document-builder/src/document-builder.model.d.ts +1 -0
  3. package/components/document-builder/src/plugins/heading/heading.plugin.d.ts +4 -0
  4. package/components/document-builder/src/plugins/{image-upload.plugin.d.ts → image-upload/image-upload.plugin.d.ts} +0 -4
  5. package/components/document-builder/src/plugins/index.d.ts +6 -5
  6. package/components/table/src/models/table-item.model.d.ts +2 -1
  7. package/components/table/src/models/table-option.model.d.ts +2 -1
  8. package/fesm2022/sd-angular-core-components-document-builder.mjs +240 -333
  9. package/fesm2022/sd-angular-core-components-document-builder.mjs.map +1 -1
  10. package/fesm2022/sd-angular-core-components-table.mjs +361 -73
  11. package/fesm2022/sd-angular-core-components-table.mjs.map +1 -1
  12. package/fesm2022/sd-angular-core-components-workflow.mjs +3 -3
  13. package/fesm2022/sd-angular-core-components-workflow.mjs.map +1 -1
  14. package/fesm2022/sd-angular-core-forms-autocomplete.mjs +4 -2
  15. package/fesm2022/sd-angular-core-forms-autocomplete.mjs.map +1 -1
  16. package/fesm2022/sd-angular-core-forms-date.mjs +4 -2
  17. package/fesm2022/sd-angular-core-forms-date.mjs.map +1 -1
  18. package/fesm2022/sd-angular-core-forms-datetime.mjs +17 -3
  19. package/fesm2022/sd-angular-core-forms-datetime.mjs.map +1 -1
  20. package/fesm2022/sd-angular-core-forms-input-number.mjs +4 -3
  21. package/fesm2022/sd-angular-core-forms-input-number.mjs.map +1 -1
  22. package/fesm2022/sd-angular-core-forms-input.mjs +4 -2
  23. package/fesm2022/sd-angular-core-forms-input.mjs.map +1 -1
  24. package/fesm2022/sd-angular-core-forms-radio.mjs +4 -2
  25. package/fesm2022/sd-angular-core-forms-radio.mjs.map +1 -1
  26. package/fesm2022/sd-angular-core-forms-select.mjs +4 -2
  27. package/fesm2022/sd-angular-core-forms-select.mjs.map +1 -1
  28. package/fesm2022/sd-angular-core-forms-textarea.mjs +14 -2
  29. package/fesm2022/sd-angular-core-forms-textarea.mjs.map +1 -1
  30. package/fesm2022/sd-angular-core-pipes.mjs +21 -1
  31. package/fesm2022/sd-angular-core-pipes.mjs.map +1 -1
  32. package/forms/datetime/src/datetime.component.d.ts +4 -1
  33. package/package.json +69 -69
  34. package/pipes/index.d.ts +1 -0
  35. package/pipes/src/empty.pipe.d.ts +7 -0
  36. /package/components/document-builder/src/plugins/{comment.plugin.d.ts → comment/comment.plugin.d.ts} +0 -0
  37. /package/components/document-builder/src/plugins/{page-orientation.plugin.d.ts → page-orientation/page-orientation.plugin.d.ts} +0 -0
  38. /package/components/document-builder/src/plugins/{table-fit.plugin.d.ts → table-fit/table-fit.plugin.d.ts} +0 -0
  39. /package/components/document-builder/src/plugins/{variable.plugin.d.ts → variable/variable.plugin.d.ts} +0 -0
@@ -6,6 +6,7 @@ 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
7
  import { Subscription, Subject, throttleTime } from 'rxjs';
8
8
  import { SdResolveMaybeAsync, hslToHex, rgbToHex, SdUtilities } from '@sd-angular/core/utilities';
9
+ import { v4 } from 'uuid';
9
10
 
10
11
  class PageNumberPlugin extends Plugin {
11
12
  init() {
@@ -129,84 +130,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImpo
129
130
  type: Output
130
131
  }] } });
131
132
 
132
- // Icon khổ dọc (Mặc định cũ)
133
- 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>';
134
- // Icon khổ ngang (Mới)
135
- const ICON_LANDSCAPE = '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M18 4H2C.9 4 0 4.9 0 6v8c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 10H2V6h16v8z"/></svg>';
136
- class PageOrientationPlugin extends Plugin {
137
- static pluginName = 'PageOrientationPlugin';
138
- _currentOrientation = 'PORTRAIT';
139
- orientationChangeEmitter;
140
- buttonView;
133
+ class HeadingPlugin extends Plugin {
141
134
  init() {
142
135
  const editor = this.editor;
143
- const componentFactory = editor.ui.componentFactory;
144
- // Đăng ký nút tên là 'pageOrientation'
145
- componentFactory.add('pageOrientation', locale => {
146
- const view = new ButtonView(locale);
147
- this.buttonView = view;
148
- view.set({
149
- // label: 'Xoay giấy (A4)',
150
- icon: ICON_PORTRAIT,
151
- // tooltip: true,
152
- // withText: true,
153
- class: 'btn-orientation', // Class để style nếu cần
154
- });
155
- // Xử lý khi bấm nút
156
- view.on('execute', () => {
157
- this.toggleOrientation();
158
- });
159
- return view;
160
- });
161
- }
162
- /**
163
- * Toggle between portrait and landscape orientation
164
- */
165
- toggleOrientation() {
166
- const newOrientation = this._currentOrientation === 'PORTRAIT' ? 'LANDSCAPE' : 'PORTRAIT';
167
- this.setOrientation(newOrientation);
168
- }
169
- /**
170
- * Set orientation programmatically
171
- */
172
- setOrientation(orientation) {
173
- const editor = this.editor;
174
- const editingView = editor.editing.view;
175
- const rootElement = editingView.document.getRoot();
176
- editor.editing.view.change(writer => {
177
- if (orientation === 'LANDSCAPE') {
178
- writer.addClass('landscape', rootElement);
179
- }
180
- else {
181
- writer.removeClass('landscape', rootElement);
182
- }
136
+ editor.conversion.for('editingDowncast').markerToHighlight({
137
+ model: 'highlightMarker',
138
+ view: {
139
+ classes: 'ck-heading-highlight',
140
+ },
183
141
  });
184
- // Update button icon
185
- if (this.buttonView) {
186
- this.buttonView.icon = orientation === 'LANDSCAPE' ? ICON_LANDSCAPE : ICON_PORTRAIT;
187
- }
188
- this._currentOrientation = orientation;
189
- this.orientationChangeEmitter?.(orientation);
190
- }
191
- /**
192
- * Get current orientation
193
- */
194
- getOrientation() {
195
- return this._currentOrientation;
196
- }
197
- /**
198
- * Register callback for orientation changes
199
- */
200
- onOrientationChange(callback) {
201
- this.orientationChangeEmitter = callback;
202
142
  }
203
143
  }
204
144
 
205
145
  class CommentPlugin extends Plugin {
206
146
  init() {
207
147
  const editor = this.editor;
208
- // --- 1. CONVERSION: MODEL MARKER -> VIEW CSS ---
209
- // Biến Marker thành Highlight màu vàng
148
+ // 1. MODEL MARKER -> VIEW CSS
210
149
  editor.conversion.for('downcast').markerToHighlight({
211
150
  model: 'comment',
212
151
  view: data => {
@@ -218,16 +157,15 @@ class CommentPlugin extends Plugin {
218
157
  };
219
158
  },
220
159
  });
221
- // --- 3. ĐĂNG KÝ UI COMPONENT: 'addCommentBtn' ---
160
+ // 2. ĐĂNG KÝ UI COMPONENT: 'addCommentBtn'
222
161
  editor.ui.componentFactory.add('addCommentBtn', locale => {
223
162
  const view = new ButtonView(locale);
224
163
  // Lấy config từ Angular
225
164
  const config = editor.config;
226
165
  const getOption = config.get('getOption');
227
166
  const option = getOption?.();
228
- // ẨN BUTTON NẾU KHÔNG onAddComment
167
+ // Ẩn button nếu không onAddComment
229
168
  if (!option?.onAddComment) {
230
- // Trả về button rỗng hoặc null để không hiển thị
231
169
  view.set({
232
170
  label: '',
233
171
  isVisible: false,
@@ -236,19 +174,18 @@ class CommentPlugin extends Plugin {
236
174
  }
237
175
  view.set({
238
176
  label: 'Thêm bình luận',
239
- // Icon SVG comment
240
177
  icon: '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M18 13v6l-4-4H4a2 2 0 01-2-2V4a2 2 0 012-2h14a2 2 0 012 2v9zM5 7h10v2H5V7zm0 4h10v2H5v-2z"/></svg>',
241
178
  tooltip: true,
242
- isEnabled: false, // Mặc định disable
179
+ isEnabled: false,
243
180
  });
244
- // 4. Logic Enable/Disable: Dựa theo Selection
181
+ // Logic Enable/Disable: Dựa theo Selection
245
182
  const selection = editor.model.document.selection;
246
183
  // Lắng nghe sự kiện change selection để bật/tắt nút
247
184
  this.listenTo(selection, 'change', () => {
248
185
  // Enable khi có bôi đen text (không phải collapsed)
249
186
  view.isEnabled = !selection.isCollapsed;
250
187
  });
251
- // 5. Logic Execute: Khi bấm nút
188
+ // Logic Execute: Khi bấm nút
252
189
  this.listenTo(view, 'execute', () => {
253
190
  const range = selection.getFirstRange();
254
191
  if (!range || !option?.onAddComment)
@@ -260,14 +197,14 @@ class CommentPlugin extends Plugin {
260
197
  selectedText += item.data;
261
198
  }
262
199
  }
263
- // BẮN EVENT RA NGOÀI - KHÔNG TỰ ADD MARKER
200
+ // BẮN EVENT RA NGOÀI - KHÔNG TỰ ADD MARKER
264
201
  // Angular component sẽ xử lý logic (mở modal, validation, etc.)
265
202
  // và gọi lại hàm addComment() nếu cần
266
203
  option.onAddComment(range);
267
204
  });
268
205
  return view;
269
206
  });
270
- // 8. Xử lý sự kiện Copy (Clipboard Output)
207
+ // 3. Xử lý sự kiện Copy (Clipboard Output)
271
208
  this.listenTo(editor.editing.view.document, 'clipboardOutput', (evt, data) => {
272
209
  const content = data.content;
273
210
  editor.editing.view.change(writer => {
@@ -309,13 +246,14 @@ class VariablePlugin extends Plugin {
309
246
  allowWhere: '$text',
310
247
  isInline: true,
311
248
  isObject: true,
312
- allowAttributes: ['id', 'value', 'display', 'data'],
249
+ allowAttributes: ['id', 'uuid', 'value', 'display', 'data'],
313
250
  });
314
251
  // 2. Conversion: Model -> View (Hiển thị ra HTML)
315
252
  conversion.for('downcast').elementToElement({
316
253
  model: 'variable',
317
254
  view: (modelItem, { writer: viewWriter }) => {
318
255
  const id = modelItem.getAttribute('id');
256
+ const uuid = modelItem.getAttribute('uuid');
319
257
  const display = modelItem.getAttribute('display');
320
258
  const value = modelItem.getAttribute('value');
321
259
  // Xử lý data (Object -> String)
@@ -324,6 +262,7 @@ class VariablePlugin extends Plugin {
324
262
  const widgetElement = viewWriter.createContainerElement('span', {
325
263
  class: 'variable-widget',
326
264
  'data-id': id,
265
+ 'data-uuid': uuid,
327
266
  'data-value': value,
328
267
  'data-display': display,
329
268
  'data-json': dataJson,
@@ -350,6 +289,7 @@ class VariablePlugin extends Plugin {
350
289
  }
351
290
  return modelWriter.createElement('variable', {
352
291
  id: viewElement.getAttribute('data-id'),
292
+ uuid: viewElement.getAttribute('data-uuid'),
353
293
  value: viewElement.getAttribute('data-value'),
354
294
  display: viewElement.getAttribute('data-display'),
355
295
  data: parsedData, // Lưu vào model dưới dạng Object gốc
@@ -362,7 +302,6 @@ class VariablePlugin extends Plugin {
362
302
  const jsonData = dataTransfer.getData('ck-variable');
363
303
  if (!jsonData)
364
304
  return;
365
- // Ngăn trình duyệt xử lý mặc định
366
305
  evt.stop();
367
306
  let variable = JSON.parse(jsonData);
368
307
  const config = editor.config;
@@ -390,17 +329,18 @@ class VariablePlugin extends Plugin {
390
329
  const viewRange = data.dropRange;
391
330
  const modelRange = editor.editing.mapper.toModelRange(viewRange);
392
331
  editor.model.change(writer => {
393
- // A. Chèn biến
332
+ // 4.1. Chèn biến
394
333
  const variableElem = writer.createElement('variable', {
395
334
  id: variable.id,
335
+ uuid: v4(),
396
336
  value: variable.value,
397
337
  display: variable.display,
398
338
  data: variable.data,
399
339
  });
400
340
  editor.model.insertContent(variableElem, modelRange);
401
- // B. Đặt con trỏ ra sau biến
341
+ // 4.2. Đặt con trỏ ra sau biến
402
342
  writer.setSelection(variableElem, 'after');
403
- // C. [QUAN TRỌNG] XÓA SẠCH CÁC MARKER DROP
343
+ // 4.3. Xóa sạch các maker drop
404
344
  // Thay vì chỉ xóa 'drop-target', ta duyệt tìm tất cả marker có tên bắt đầu bằng 'drop-target'
405
345
  // Vì đôi khi CKEditor tạo ra các biến thể khác nhau
406
346
  for (const marker of editor.model.markers) {
@@ -409,11 +349,10 @@ class VariablePlugin extends Plugin {
409
349
  }
410
350
  }
411
351
  });
412
- // FIX LỖI: Focus lại vào editor để xóa các artifact của việc kéo thả
352
+ // 4.4. Focus lại vào editor để xóa các artifact của việc kéo thả
413
353
  editor.editing.view.focus();
414
- // [BỔ SUNG] XÓA CLASS RÁC TRÊN VIEW (NẾU MARKER KHÔNG HẾT)
415
- // Đôi khi View chưa kịp render lại, ta ép xóa class thủ công trên root nếu cần
416
- // (Thường bước C ở trên là đủ, nhưng đây là chốt chặn cuối cùng bằng JS)
354
+ // 4.5. Xóa class rác trên view
355
+ // Đôi khi View chưa kịp render lại, ta ép xóa class thủ công trên root
417
356
  const viewRoot = editor.editing.view.document.getRoot();
418
357
  if (viewRoot) {
419
358
  editor.editing.view.change(viewWriter => {
@@ -601,25 +540,17 @@ class TableFitPlugin extends Plugin {
601
540
  }
602
541
  }
603
542
 
604
- /**
605
- * Custom base64 upload adapter plugin for CKEditor 5.
606
- * Converts uploaded images to base64 data URLs instead of uploading to a server.
607
- */
608
543
  class ImageUploadPlugin extends Plugin {
609
544
  static get pluginName() {
610
545
  return 'ImageUploadPlugin';
611
546
  }
612
547
  init() {
613
548
  const editor = this.editor;
614
- // Register the custom upload adapter
615
549
  editor.plugins.get('FileRepository').createUploadAdapter = (loader) => {
616
550
  return new Base64UploadAdapter(loader);
617
551
  };
618
552
  }
619
553
  }
620
- /**
621
- * Custom upload adapter that converts images to base64 data URLs.
622
- */
623
554
  class Base64UploadAdapter {
624
555
  loader;
625
556
  constructor(loader) {
@@ -653,6 +584,79 @@ class Base64UploadAdapter {
653
584
  }
654
585
  }
655
586
 
587
+ // Icon khổ dọc (Mặc định cũ)
588
+ 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>';
589
+ // Icon khổ ngang (Mới)
590
+ const ICON_LANDSCAPE = '<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M18 4H2C.9 4 0 4.9 0 6v8c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 10H2V6h16v8z"/></svg>';
591
+ class PageOrientationPlugin extends Plugin {
592
+ static pluginName = 'PageOrientationPlugin';
593
+ _currentOrientation = 'PORTRAIT';
594
+ orientationChangeEmitter;
595
+ buttonView;
596
+ init() {
597
+ const editor = this.editor;
598
+ const componentFactory = editor.ui.componentFactory;
599
+ // Đăng ký nút tên là 'pageOrientation'
600
+ componentFactory.add('pageOrientation', locale => {
601
+ const view = new ButtonView(locale);
602
+ this.buttonView = view;
603
+ view.set({
604
+ // label: 'Xoay giấy (A4)',
605
+ icon: ICON_PORTRAIT,
606
+ // tooltip: true,
607
+ // withText: true,
608
+ class: 'btn-orientation', // Class để style nếu cần
609
+ });
610
+ // Xử lý khi bấm nút
611
+ view.on('execute', () => {
612
+ this.toggleOrientation();
613
+ });
614
+ return view;
615
+ });
616
+ }
617
+ /**
618
+ * Toggle between portrait and landscape orientation
619
+ */
620
+ toggleOrientation() {
621
+ const newOrientation = this._currentOrientation === 'PORTRAIT' ? 'LANDSCAPE' : 'PORTRAIT';
622
+ this.setOrientation(newOrientation);
623
+ }
624
+ /**
625
+ * Set orientation programmatically
626
+ */
627
+ setOrientation(orientation) {
628
+ const editor = this.editor;
629
+ const editingView = editor.editing.view;
630
+ const rootElement = editingView.document.getRoot();
631
+ editor.editing.view.change(writer => {
632
+ if (orientation === 'LANDSCAPE') {
633
+ writer.addClass('landscape', rootElement);
634
+ }
635
+ else {
636
+ writer.removeClass('landscape', rootElement);
637
+ }
638
+ });
639
+ // Update button icon
640
+ if (this.buttonView) {
641
+ this.buttonView.icon = orientation === 'LANDSCAPE' ? ICON_LANDSCAPE : ICON_PORTRAIT;
642
+ }
643
+ this._currentOrientation = orientation;
644
+ this.orientationChangeEmitter?.(orientation);
645
+ }
646
+ /**
647
+ * Get current orientation
648
+ */
649
+ getOrientation() {
650
+ return this._currentOrientation;
651
+ }
652
+ /**
653
+ * Register callback for orientation changes
654
+ */
655
+ onOrientationChange(callback) {
656
+ this.orientationChangeEmitter = callback;
657
+ }
658
+ }
659
+
656
660
  /**
657
661
  * Cấu hình màu cho Document Builder
658
662
  * 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
@@ -844,21 +848,24 @@ function normalize(content) {
844
848
  }
845
849
 
846
850
  class SdDocumentBuilder {
847
- #id = '1212';
848
- // Shared color palette and configuration
849
- #sharedColors = getPresetColors();
850
- #colorPickerConfig = getColorPickerConfig();
851
- #fontSizeOptions = getFontSizeOptions();
852
- #headingOptions = getHeadingOptions();
853
851
  option;
854
852
  disabled = false;
855
853
  set _disabled(val) {
856
854
  this.disabled = val === '' || !!val;
857
855
  this.#updateState();
858
856
  }
857
+ contentChange = new EventEmitter(); // Emit HTML content
859
858
  Editor = ClassicEditor;
860
859
  #editor;
860
+ #id = '55b0afb0-288d-423c-98b3-5f9db286e16d';
861
+ #subscription = new Subscription();
862
+ #sharedColors = getPresetColors();
863
+ #headingOptions = getHeadingOptions();
864
+ #fontSizeOptions = getFontSizeOptions();
865
+ #colorPickerConfig = getColorPickerConfig();
866
+ #contentChangeSubject = new Subject();
861
867
  #idTimeOutScrollHeading = null;
868
+ #headingElementsMap = new Map(); // Hash lưu trữ các heading
862
869
  // Config
863
870
  config = {
864
871
  getOption: () => this.option,
@@ -895,11 +902,12 @@ class SdDocumentBuilder {
895
902
  ImageResize,
896
903
  ImageStyle,
897
904
  // Custom Plugin
898
- PageOrientationPlugin,
905
+ HeadingPlugin,
899
906
  CommentPlugin,
900
907
  VariablePlugin,
901
908
  TableFitPlugin,
902
909
  ImageUploadPlugin,
910
+ PageOrientationPlugin,
903
911
  ],
904
912
  toolbar: {
905
913
  items: [
@@ -957,14 +965,7 @@ class SdDocumentBuilder {
957
965
  colors: this.#sharedColors,
958
966
  },
959
967
  table: {
960
- contentToolbar: [
961
- 'tableColumn',
962
- 'tableRow',
963
- 'mergeTableCells',
964
- '|',
965
- 'tableProperties', // <--- Nút chỉnh thuộc tính bảng (Viền, Màu, Width)
966
- 'tableCellProperties',
967
- ],
968
+ contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', '|', 'tableProperties', 'tableCellProperties'],
968
969
  tableProperties: {
969
970
  borderColors: this.#sharedColors,
970
971
  colorPicker: this.#colorPickerConfig,
@@ -989,12 +990,8 @@ class SdDocumentBuilder {
989
990
  ],
990
991
  },
991
992
  };
992
- contentChange = new EventEmitter(); // Emit HTML content
993
- #subscription = new Subscription();
994
- #contentChangeSubject = new Subject();
995
993
  ngOnInit() {
996
- // https://onemount.atlassian.net/browse/SM-1862
997
- // Debounce trong rxjs không hỗ trợ leading -->
994
+ // Debounce trong rxjs không hỗ trợ leading --> throttleTime
998
995
  this.#subscription.add(this.#contentChangeSubject.pipe(throttleTime(500, undefined, { leading: true, trailing: true })).subscribe(content => {
999
996
  this.contentChange.emit(normalize(content));
1000
997
  }));
@@ -1004,12 +1001,6 @@ class SdDocumentBuilder {
1004
1001
  }
1005
1002
  onReady(editor) {
1006
1003
  this.#editor = editor;
1007
- editor.conversion.for('editingDowncast').markerToHighlight({
1008
- model: 'highlightMarker',
1009
- view: {
1010
- classes: 'ck-heading-highlight',
1011
- },
1012
- });
1013
1004
  // Setup orientation plugin callback
1014
1005
  try {
1015
1006
  const orientationPlugin = editor.plugins.get('PageOrientationPlugin');
@@ -1026,69 +1017,17 @@ class SdDocumentBuilder {
1026
1017
  catch (error) {
1027
1018
  console.warn('PageOrientationPlugin not available:', error);
1028
1019
  }
1029
- // Đăng ký sự kiện lắng nghe Selection để làm Comment
1020
+ // Lắng nghe selection
1030
1021
  editor.model.document.selection.on('change', $event => {
1031
1022
  this.option.onSelection?.(this.#editor.model.document.selection, $event);
1032
1023
  });
1033
- // ĐĂNG SỰ KIỆN LẮNG NGHE THAY ĐỔI NỘI DUNG
1024
+ // Lắng nghe sự kiện thay đổi nội dung
1034
1025
  editor.model.document.on('change:data', () => {
1035
1026
  const content = editor.getData();
1036
1027
  this.#contentChangeSubject.next(content);
1037
1028
  });
1038
1029
  this.#updateState();
1039
1030
  }
1040
- #updateState() {
1041
- if (!this.#editor)
1042
- return;
1043
- if (this.disabled) {
1044
- // Bật chế độ chỉ đọc với ID khóa
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
- }
1056
- }
1057
- else {
1058
- // Tắt chế độ chỉ đọc với ID khóa tương ứng
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
- }
1070
- }
1071
- }
1072
- scrollToComment = (markerId) => {
1073
- if (!this.#editor)
1074
- return;
1075
- const editor = this.#editor;
1076
- const marker = editor.model.markers.get(markerId);
1077
- if (marker) {
1078
- // 1. Set Selection vào Marker đó trước
1079
- this.#editor.model.change(writer => {
1080
- writer.setSelection(marker.getRange());
1081
- });
1082
- // 2. Sau đó gọi scrollToTheSelection
1083
- this.#editor.editing.view.scrollToTheSelection({
1084
- alignToTop: true,
1085
- });
1086
- editor.editing.view.focus();
1087
- }
1088
- else {
1089
- console.warn(`Marker with id ${markerId} not found.`);
1090
- }
1091
- };
1092
1031
  setContent = (html) => {
1093
1032
  this.#editor?.setData?.(html);
1094
1033
  };
@@ -1125,55 +1064,6 @@ class SdDocumentBuilder {
1125
1064
  }
1126
1065
  return 'PORTRAIT';
1127
1066
  };
1128
- getVariables = () => {
1129
- if (!this.#editor)
1130
- return [];
1131
- const model = this.#editor.model;
1132
- const root = model.document.getRoot();
1133
- if (!root)
1134
- return [];
1135
- const variables = [];
1136
- // Duyệt qua tất cả các phần tử trong range
1137
- // range.getItems() sẽ trả về từng node (text, element...)
1138
- try {
1139
- const range = model.createRangeIn(root);
1140
- for (const item of range.getItems()) {
1141
- // Sử dụng item.is('element', 'variable') là chính xác
1142
- if (item.is('element', 'variable')) {
1143
- variables.push({
1144
- id: item.getAttribute('id'),
1145
- value: item.getAttribute('value'),
1146
- display: item.getAttribute('display'),
1147
- data: item.getAttribute('data'),
1148
- });
1149
- }
1150
- }
1151
- }
1152
- catch (e) {
1153
- console.error(e);
1154
- return [];
1155
- }
1156
- return variables;
1157
- };
1158
- getComments() {
1159
- if (!this.#editor)
1160
- return [];
1161
- const markers = this.#editor.model.markers;
1162
- const comments = [];
1163
- // Duyệt qua tất cả markers trong Model
1164
- for (const marker of markers) {
1165
- // Chỉ lấy marker do plugin comment tạo ra (prefix 'comment:')
1166
- if (marker.name.startsWith('comment:')) {
1167
- // Lấy text nằm trong vùng marker đó
1168
- const currentText = this.#getTextFromRange(marker.getRange());
1169
- comments.push({
1170
- markerId: marker.name,
1171
- selectedText: currentText,
1172
- });
1173
- }
1174
- }
1175
- return comments;
1176
- }
1177
1067
  scrollToTop() {
1178
1068
  setTimeout(() => {
1179
1069
  if (this.#editor) {
@@ -1193,13 +1083,66 @@ class SdDocumentBuilder {
1193
1083
  }
1194
1084
  }, 100);
1195
1085
  }
1196
- // Kho lưu trữ tham chiếu Model của Heading (để phục vụ scroll)
1197
- #headingElementsMap = new Map();
1086
+ #updateState() {
1087
+ if (!this.#editor)
1088
+ return;
1089
+ if (this.disabled) {
1090
+ // Bật chế độ chỉ đọc với ID khóa
1091
+ this.#editor.enableReadOnlyMode(this.#id);
1092
+ // Disable page orientation button
1093
+ try {
1094
+ const orientationPlugin = this.#editor.plugins.get('PageOrientationPlugin');
1095
+ if (orientationPlugin && orientationPlugin.buttonView) {
1096
+ orientationPlugin.buttonView.isEnabled = false;
1097
+ }
1098
+ }
1099
+ catch (error) {
1100
+ console.warn('Failed to disable orientation button:', error);
1101
+ }
1102
+ }
1103
+ else {
1104
+ // Tắt chế độ chỉ đọc với ID khóa tương ứng
1105
+ this.#editor.disableReadOnlyMode(this.#id);
1106
+ // Enable page orientation button
1107
+ try {
1108
+ const orientationPlugin = this.#editor.plugins.get('PageOrientationPlugin');
1109
+ if (orientationPlugin && orientationPlugin.buttonView) {
1110
+ orientationPlugin.buttonView.isEnabled = true;
1111
+ }
1112
+ }
1113
+ catch (error) {
1114
+ console.warn('Failed to enable orientation button:', error);
1115
+ }
1116
+ }
1117
+ }
1118
+ #getTextFromElement = (element) => {
1119
+ let text = '';
1120
+ // Heading trong Model chứa các text node con
1121
+ for (const child of element.getChildren()) {
1122
+ if (child.is('$text') || child.is('$textProxy')) {
1123
+ text += child.data;
1124
+ }
1125
+ }
1126
+ return text;
1127
+ };
1128
+ #getTextFromRange = (range) => {
1129
+ let text = '';
1130
+ for (const item of range.getItems()) {
1131
+ // TextProxy là một phần của Text Node nằm trong Range
1132
+ if (item.is('$textProxy') || item.is('$text')) {
1133
+ text += item.data;
1134
+ }
1135
+ }
1136
+ return text;
1137
+ };
1138
+ // ========================================================================
1139
+ // 1. QUẢN LÝ HEADING
1140
+ // ========================================================================
1198
1141
  heading = {
1199
1142
  // ========================================================================
1200
1143
  // HÀM LẤY DANH SÁCH HEADING (TOC)
1201
1144
  // ========================================================================
1202
- getHeadings: () => {
1145
+ all: () => {
1203
1146
  if (!this.#editor)
1204
1147
  return [];
1205
1148
  const root = this.#editor.model.document.getRoot();
@@ -1235,10 +1178,7 @@ class SdDocumentBuilder {
1235
1178
  }
1236
1179
  return headings;
1237
1180
  },
1238
- // ========================================================================
1239
- // HÀM SCROLL TO HEADING
1240
- // ========================================================================
1241
- scrollToHeading: (id) => {
1181
+ scroll: (id) => {
1242
1182
  if (!this.#editor)
1243
1183
  return;
1244
1184
  const modelElement = this.#headingElementsMap.get(id);
@@ -1277,7 +1217,7 @@ class SdDocumentBuilder {
1277
1217
  writer.removeMarker(marker);
1278
1218
  });
1279
1219
  }
1280
- }, 10000);
1220
+ }, 5000);
1281
1221
  }
1282
1222
  else {
1283
1223
  console.warn(`Heading with id ${id} not found.`);
@@ -1285,30 +1225,7 @@ class SdDocumentBuilder {
1285
1225
  },
1286
1226
  };
1287
1227
  // ========================================================================
1288
- // HELPER: LẤY TEXT TỪ ELEMENT (Đệ quy nhẹ)
1289
- // ========================================================================
1290
- #getTextFromElement = (element) => {
1291
- let text = '';
1292
- // Heading trong Model chứa các text node con
1293
- for (const child of element.getChildren()) {
1294
- if (child.is('$text') || child.is('$textProxy')) {
1295
- text += child.data;
1296
- }
1297
- }
1298
- return text;
1299
- };
1300
- #getTextFromRange = (range) => {
1301
- let text = '';
1302
- for (const item of range.getItems()) {
1303
- // TextProxy là một phần của Text Node nằm trong Range
1304
- if (item.is('$textProxy') || item.is('$text')) {
1305
- text += item.data;
1306
- }
1307
- }
1308
- return text;
1309
- };
1310
- // ========================================================================
1311
- // COMMENT MANAGEMENT
1228
+ // 2. QUẢN COMMENT
1312
1229
  // ========================================================================
1313
1230
  comment = {
1314
1231
  /**
@@ -1454,72 +1371,71 @@ class SdDocumentBuilder {
1454
1371
  }
1455
1372
  },
1456
1373
  };
1457
- // exportDocx(fileName: string = 'document.docx'): void {
1458
- // if (!this.#editor) return;
1459
- // // 1. Kiểm tra xem Editor đang ở chế độ Landscape hay Portrait
1460
- // // (Dựa vào class 'landscape' mà Plugin PageOrientation đã toggle trên View)
1461
- // const rootElement = this.#editor.editing.view.document.getRoot();
1462
- // const isLandscape = rootElement?.hasClass('landscape');
1463
- // const orientation = isLandscape ? 'landscape' : 'portrait';
1464
- // // 2. Lấy nội dung HTML
1465
- // const contentHtml = this.#editor.getData();
1466
- // // 3. Chuẩn bị HTML với CSS @page động
1467
- // const fullHtml = `
1468
- // <!DOCTYPE html>
1469
- // <html>
1470
- // <head>
1471
- // <meta charset="UTF-8">
1472
- // <style>
1473
- // /* --- CẤU HÌNH KHỔ GIẤY DỰA TRÊN TRẠNG THÁI --- */
1474
- // @page {
1475
- // size: A4 ${orientation}; /* Thêm portrait hoặc landscape vào đây */
1476
- // margin: 20mm;
1477
- // }
1478
- // body {
1479
- // font-family: 'Times New Roman', serif;
1480
- // font-size: 13pt;
1481
- // line-height: 1.5;
1482
- // }
1483
- // /* --- STYLE CHO BẢNG BIỂU --- */
1484
- // table {
1485
- // width: 100%;
1486
- // border-collapse: collapse;
1487
- // }
1488
- // td, th {
1489
- // border: 1px solid black;
1490
- // padding: 5px;
1491
- // }
1492
- // /* --- STYLE CHO BIẾN --- */
1493
- // .variable-widget {
1494
- // color: #1565c0;
1495
- // background-color: #e3f2fd;
1496
- // font-weight: bold;
1497
- // border: 1px solid #90caf9;
1498
- // padding: 0 4px;
1499
- // border-radius: 4px;
1500
- // }
1501
- // /* --- ẨN COMMENT KHI IN --- */
1502
- // .ck-comment-marker {
1503
- // background-color: transparent;
1504
- // border: none;
1505
- // }
1506
- // </style>
1507
- // </head>
1508
- // <body>
1509
- // ${contentHtml}
1510
- // </body>
1511
- // </html>
1512
- // `;
1513
- // // 3. Convert sang Blob (Dạng file Binary)
1514
- // asBlob(fullHtml, {
1515
- // orientation: 'portrait', // 'portrait' hoặc 'landscape'
1516
- // margins: { top: 720, right: 720, bottom: 720, left: 720 }, // Đơn vị twips (1440 twips = 1 inch)
1517
- // }).then(blob => {
1518
- // if (blob instanceof Blob) {
1519
- // SdUtilities.downloadBlob(blob, fileName);
1520
- // }
1521
- // });
1522
- // }
1374
+ // ========================================================================
1375
+ // 3. QUẢN LÝ VARIABLE
1376
+ // ========================================================================
1377
+ variable = {
1378
+ all: () => {
1379
+ if (!this.#editor)
1380
+ return [];
1381
+ const model = this.#editor.model;
1382
+ const root = model.document.getRoot();
1383
+ if (!root)
1384
+ return [];
1385
+ const variables = [];
1386
+ try {
1387
+ const range = model.createRangeIn(root);
1388
+ for (const item of range.getItems()) {
1389
+ // Sử dụng item.is('element', 'variable') là chính xác
1390
+ if (item.is('element', 'variable')) {
1391
+ variables.push({
1392
+ id: item.getAttribute('id'),
1393
+ uuid: item.getAttribute('uuid'),
1394
+ value: item.getAttribute('value'),
1395
+ display: item.getAttribute('display'),
1396
+ data: item.getAttribute('data'),
1397
+ });
1398
+ }
1399
+ }
1400
+ }
1401
+ catch (e) {
1402
+ console.error(e);
1403
+ return [];
1404
+ }
1405
+ return variables;
1406
+ },
1407
+ scroll: (uuid) => {
1408
+ if (!this.#editor)
1409
+ return;
1410
+ const model = this.#editor.model;
1411
+ const root = model.document.getRoot();
1412
+ if (!root)
1413
+ return;
1414
+ let targetElement = null;
1415
+ const range = model.createRangeIn(root);
1416
+ for (const item of range.getItems()) {
1417
+ if (item.is('element', 'variable') && item.getAttribute('uuid') === uuid) {
1418
+ targetElement = item;
1419
+ break;
1420
+ }
1421
+ }
1422
+ if (targetElement) {
1423
+ const viewElement = this.#editor.editing.mapper.toViewElement(targetElement);
1424
+ if (viewElement) {
1425
+ const domElement = this.#editor.editing.view.domConverter.viewToDom(viewElement);
1426
+ if (domElement) {
1427
+ domElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
1428
+ model.change(writer => {
1429
+ writer.setSelection(targetElement, 'on');
1430
+ });
1431
+ }
1432
+ }
1433
+ }
1434
+ else {
1435
+ console.warn(`Variable với id ${uuid} không tìm thấy trong tài liệu.`);
1436
+ }
1437
+ },
1438
+ };
1523
1439
  // ========================================================================
1524
1440
  // 4. HÀM EXPORT DOCX (FULL HEADER/FOOTER + PAGE NUMBER)
1525
1441
  // ========================================================================
@@ -1624,22 +1540,13 @@ class SdDocumentBuilder {
1624
1540
  // Thêm '\ufeff' (BOM) để fix lỗi font tiếng Việt
1625
1541
  const blob = new Blob(['\ufeff', fullHtml], { type: 'application/msword' });
1626
1542
  SdUtilities.downloadBlob(blob, fileName);
1627
- // 3. Convert & Save
1628
- // asBlob(fullHtml, {
1629
- // orientation: orientation as 'portrait' | 'landscape',
1630
- // margins: { top: 720, right: 720, bottom: 720, left: 720 },
1631
- // }).then(blob => {
1632
- // if (blob instanceof Blob) {
1633
- // SdUtilities.downloadBlob(blob, fileName);
1634
- // }
1635
- // });
1636
1543
  }
1637
1544
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdDocumentBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
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"] }] });
1545
+ 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;animation:fadeOut 2s 8s forwards}@keyframes fadeOut{0%{background-color:#fef08a}to{background-color:transparent}}\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\";: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}:host ::ng-deep .ck.ck-clipboard-drop-target-line{display: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"] }] });
1639
1546
  }
1640
1547
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.17", ngImport: i0, type: SdDocumentBuilder, decorators: [{
1641
1548
  type: Component,
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"] }]
1549
+ 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;animation:fadeOut 2s 8s forwards}@keyframes fadeOut{0%{background-color:#fef08a}to{background-color:transparent}}\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\";: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}:host ::ng-deep .ck.ck-clipboard-drop-target-line{display: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"] }]
1643
1550
  }], propDecorators: { option: [{
1644
1551
  type: Input,
1645
1552
  args: [{ required: true }]