@sd-angular/core 19.0.0-beta.88 → 19.0.0-beta.89

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,3 +43,9 @@ export declare class VariablePlugin extends Plugin {
43
43
  /** Xóa toàn bộ binding values trong document. Shorthand của clearValues(). */
44
44
  clearAllValues(): void;
45
45
  }
46
+ /**
47
+ * HTML từ getData() phiên bản cũ có thể chứa block (vd. table) bên trong `span.variable-widget[data-binding="html"]`,
48
+ * khiến setData/upcast CKEditor lỗi (unexpected-error, null.start). Gọi trước `setData` để giữ chỉ
49
+ * `data-binding-value` và bỏ các node con.
50
+ */
51
+ export declare function sanitizeVariableHtmlBoundSerializedHtml(html: string): string;
@@ -1,12 +1,19 @@
1
1
  import { SdButton } from '@sd-angular/core/components/button';
2
2
  import { SdUnwrapSignal } from '@sd-angular/core/utilities/models';
3
3
  export interface SdTableOptionSelector<T = any> {
4
+ /** Bật/tắt cột checkbox selector. Mặc định: true. */
4
5
  visible?: boolean;
6
+ /** Chỉ cho chọn 1 dòng tại một thời điểm. */
5
7
  single?: boolean;
8
+ /** Danh sách action hiển thị khi có item được chọn. */
6
9
  actions?: SdTableAction<T>[];
10
+ /** Nội dung hiển thị bên cạnh selector/action bar. */
7
11
  message?: string | ((selectedItems?: T[]) => string);
12
+ /** Callback khi user chọn/bỏ chọn một dòng. */
8
13
  onSelect?: (rowData?: T, selectedItems?: T[]) => void;
14
+ /** Callback khi user chọn tất cả hoặc bỏ chọn tất cả. */
9
15
  onSelectAll?: (selectedItems: T[]) => void;
16
+ /** Disable checkbox của từng dòng theo điều kiện. */
10
17
  disabled?: (rowData?: T, selectedItems?: T[]) => boolean;
11
18
  /**
12
19
  * Predicate để tự động pre-select item sau mỗi lần load.
@@ -17,25 +24,66 @@ export interface SdTableOptionSelector<T = any> {
17
24
  }
18
25
  export type SdTableAction<T = any> = SdTableActionNormal<T> | SdTableActionChildren<T>;
19
26
  export interface SdTableActionNormal<T = any> {
27
+ /**
28
+ * Tên icon hiển thị trên action button.
29
+ * Giá trị này được render qua `mat-icon`, vì vậy nên dùng đúng tên glyph của Material Icons.
30
+ * Tra cứu icon tại: https://fonts.google.com/icons
31
+ */
20
32
  icon?: string;
33
+ /**
34
+ * Font set của Material icon.
35
+ * Kiểu thực tế lấy từ `SdButton['fontSet']` và chỉ hỗ trợ các giá trị thuộc `MaterialIconFontSet`,
36
+ * ví dụ: `material-icons`, `material-icons-outlined`, `material-icons-round`,
37
+ * `material-icons-sharp`, `material-symbols-outlined`.
38
+ * Nếu không truyền, `SdButton` sẽ dùng font set mặc định của nó.
39
+ */
21
40
  fontSet?: SdUnwrapSignal<SdButton['fontSet']>;
41
+ /** Tooltip hiển thị khi hover action button. */
22
42
  tooltip?: SdUnwrapSignal<SdButton['tooltip']>;
43
+ /** Text label hiển thị trên button action. */
23
44
  title?: SdUnwrapSignal<SdButton['title']>;
45
+ /**
46
+ * Màu của `SdButton`.
47
+ * Kiểu thực tế map theo `SdButton['color']`, hiện dùng các token như
48
+ * `primary`, `secondary`, `info`, `success`, `warning`, `error`.
49
+ */
24
50
  color?: SdUnwrapSignal<SdButton['color']>;
51
+ /**
52
+ * Variant hiển thị của `SdButton`.
53
+ * Kiểu thực tế là `fill | light | outline | link`.
54
+ */
25
55
  type?: SdUnwrapSignal<SdButton['type']>;
56
+ /** Ẩn action theo cờ tĩnh hoặc theo điều kiện của dữ liệu dòng. */
26
57
  hidden?: boolean | ((rowData?: T) => boolean);
58
+ /** Gom action vào nhóm hiển thị compact nếu table hỗ trợ grouped actions. */
27
59
  isGrouped?: boolean;
60
+ /** Hàm xử lý khi click action, nhận toàn bộ item đang được chọn. */
28
61
  click: (selectedItems?: T[]) => void;
29
62
  }
30
63
  interface SdTableActionChildren<T = any> {
64
+ /**
65
+ * Tên icon của action cha (group action).
66
+ * Giá trị nên là tên glyph Material Icons: https://fonts.google.com/icons
67
+ */
31
68
  icon?: string;
69
+ /**
70
+ * Font set của Material icon cho action cha.
71
+ * Dùng cùng kiểu dữ liệu với `SdButton['fontSet']`.
72
+ */
32
73
  fontSet?: SdUnwrapSignal<SdButton['fontSet']>;
74
+ /** Tooltip của action cha. */
33
75
  tooltip?: SdUnwrapSignal<SdButton['tooltip']>;
76
+ /** Label của action cha. */
34
77
  title?: SdUnwrapSignal<SdButton['title']>;
78
+ /** Màu của action cha theo kiểu `SdButton['color']`. */
35
79
  color?: SdUnwrapSignal<SdButton['color']>;
80
+ /** Variant hiển thị của action cha theo kiểu `SdButton['type']`. */
36
81
  type?: SdUnwrapSignal<SdButton['type']>;
82
+ /** Ẩn action cha theo cờ tĩnh hoặc theo điều kiện của dữ liệu dòng. */
37
83
  hidden?: boolean | ((rowData?: T) => boolean);
84
+ /** Gom action cha vào nhóm hiển thị compact nếu table hỗ trợ grouped actions. */
38
85
  isGrouped?: boolean;
86
+ /** Danh sách action con hiển thị bên trong group action này. */
39
87
  children: SdTableActionNormal<T>[];
40
88
  }
41
89
  export {};
@@ -13,38 +13,140 @@ import { SdTableOptionSort } from './table-option-sort.model';
13
13
  import { SdTableOptionStyle } from './table-option-style.model';
14
14
  export type SdTableOption<T = any> = SdTableLocalOption<T> | SdTableServerOption<T>;
15
15
  interface SdTableBaseOption<T = any> {
16
+ /**
17
+ * Key định danh của table option.
18
+ * Thường dùng khi một số tính năng con cần lưu trạng thái riêng theo table,
19
+ * ví dụ các option có hỗ trợ cache/config theo key.
20
+ */
16
21
  key?: string;
22
+ /**
23
+ * Cấu hình hiển thị/phục hồi config của table.
24
+ * Xem thêm kiểu chi tiết tại `TableOptionConfig`.
25
+ */
17
26
  config?: TableOptionConfig;
27
+ /**
28
+ * Cấu hình chọn dòng (checkbox selector, action theo selection, pre-select...).
29
+ * Xem thêm tại `SdTableOptionSelector`.
30
+ */
18
31
  selector?: SdTableOptionSelector<T>;
32
+ /**
33
+ * Cấu hình expand row.
34
+ * Xem thêm tại `SdTableOptionExpand` để biết cách disable, expand nhiều dòng,
35
+ * hoặc luôn hiển thị vùng expand.
36
+ */
19
37
  expand?: SdTableOptionExpand<T>;
38
+ /**
39
+ * Bật/tắt sorting ở cấp table.
40
+ * Xem thêm tại `SdTableOptionSort`.
41
+ */
20
42
  sort?: SdTableOptionSort;
43
+ /**
44
+ * Cấu hình phân trang.
45
+ * Xem thêm tại `SdTableOptionPaginate`.
46
+ */
21
47
  paginate?: SdTableOptionPaginate;
48
+ /**
49
+ * Cấu hình hành vi reload dữ liệu.
50
+ * Xem thêm tại `SdTableOptionReload`.
51
+ */
22
52
  reload?: SdTableOptionReload<T>;
53
+ /**
54
+ * Cấu hình export dữ liệu.
55
+ * Xem thêm tại `SdTableOptionExport` để biết mode `default`/`custom`, mapping,
56
+ * sheet export và giới hạn số dòng.
57
+ */
23
58
  export?: SdTableOptionExport<T>;
59
+ /**
60
+ * Cấu hình group dữ liệu theo nhiều field.
61
+ * Xem thêm tại `SdTableOptionGroup`.
62
+ */
24
63
  group?: SdTableOptionGroup<T>;
64
+ /**
65
+ * Cấu hình filter của table.
66
+ * Xem thêm tại `SdTableOptionFilter` để biết filter inline, external filter,
67
+ * manual filter, cacheable và callback clear filter.
68
+ */
25
69
  filter?: SdTableOptionFilter;
70
+ /**
71
+ * Cấu hình kéo-thả đổi thứ tự dòng.
72
+ * Chỉ bật khi table cho phép reorder dữ liệu phía client.
73
+ */
26
74
  rowReorder?: {
75
+ /** Bật/tắt tính năng reorder row. */
27
76
  enabled?: boolean;
77
+ /**
78
+ * Callback sau khi reorder xong.
79
+ * `newRows` là mảng sau khi đã đổi vị trí ở UI,
80
+ * `movedItem` là item vừa được kéo,
81
+ * `fromIndex` và `toIndex` là vị trí cũ/mới.
82
+ */
28
83
  onChange?: (newRows: T[], movedItem: T, fromIndex: number, toIndex: number) => void;
84
+ /**
85
+ * Tên icon hiển thị cho drag handle.
86
+ * Nếu không truyền thì table dùng icon reorder mặc định của Material.
87
+ * Nếu truyền custom icon, nên dùng tên glyph của Material Icons:
88
+ * https://fonts.google.com/icons
89
+ */
29
90
  icon?: string;
91
+ /**
92
+ * Disable khả năng reorder theo từng dòng.
93
+ * Trả về `true` để chặn kéo-thả dòng tương ứng.
94
+ */
30
95
  disabled?: (row: T, index: number) => boolean;
31
96
  };
97
+ /**
98
+ * Danh sách command thao tác theo từng dòng.
99
+ * Xem thêm tại `SdTableCommand`.
100
+ */
32
101
  commands?: SdTableCommand<T>[];
102
+ /**
103
+ * Cấu hình vùng command tập trung.
104
+ * Dùng khi cần set thêm alignment hoặc bọc danh sách command trong object option.
105
+ * Xem thêm tại `SdTableCommandOption`.
106
+ */
33
107
  command?: SdTableCommandOption<T>;
108
+ /**
109
+ * Danh sách cột hiển thị của table.
110
+ * Đây là phần bắt buộc. Xem thêm tại `SdTableColumn` để cấu hình field, filter,
111
+ * sort, transform, template và export của từng cột.
112
+ */
34
113
  columns: SdTableColumn<T>[];
114
+ /**
115
+ * Cấu hình style/layout chung của table.
116
+ * Xem thêm tại `SdTableOptionStyle`.
117
+ */
35
118
  style?: SdTableOptionStyle<T>;
36
119
  }
37
120
  interface SdTableLocalOption<T = any> extends SdTableBaseOption<T> {
121
+ /** Dùng dữ liệu local tại client. */
38
122
  type: 'local';
123
+ /**
124
+ * Hàm trả dữ liệu local cho table.
125
+ * Table sẽ tự gọi hàm này để lấy mảng item nguồn.
126
+ * Có thể trả về đồng bộ hoặc `Promise<T[]>`.
127
+ */
39
128
  items: () => T[] | Promise<T[]>;
40
129
  }
41
130
  interface SdTableServerOption<T = any> extends SdTableBaseOption<T> {
131
+ /** Dùng dữ liệu từ server, có paging/filter/sort theo request của table. */
42
132
  type: 'server';
133
+ /**
134
+ * Hàm lấy dữ liệu từ server.
135
+ * `filterRequest` chứa trạng thái filter/sort/visible columns ở dạng của table.
136
+ * `pagingReq` là dữ liệu paging đã được chuẩn hóa để dễ map sang API backend.
137
+ * Hàm cần trả về `{ items, total }` để table render và tính phân trang.
138
+ */
43
139
  items: (filterRequest: SdTableFilterRequest<T>, pagingReq: SdPagingReq<T>) => Promise<{
44
140
  items: T[];
45
141
  total: number;
46
142
  }>;
143
+ /**
144
+ * Hook chạy khi table phát sinh filter request.
145
+ * Dùng để can thiệp thêm trước/sau khi gọi API, ví dụ sync external state,
146
+ * theo dõi trạng thái valid của external filter, hoặc ghi log request filter hiện tại.
147
+ */
47
148
  onFilter?: (filterRequest: SdTableFilterRequest<T>, args: {
149
+ /** Cho biết toàn bộ external filter hiện tại có hợp lệ để trigger filter hay không. */
48
150
  externalFilterValid: boolean;
49
151
  }) => void;
50
152
  }
@@ -415,12 +415,12 @@ class VariablePlugin extends Plugin {
415
415
  return toWidget(widgetElement, viewWriter);
416
416
  },
417
417
  });
418
- // 2b. One-way attribute converter cho bindingValue (chỉ editing view, không ảnh hưởng getData())
419
- // Chỉ chạy khi bindValue() / clearValue() gọi model.change setAttribute/removeAttribute
420
- // Binding được persist vào HTML (getData() setData() binding state)
421
- // conversion.for('downcast') ảnh hưởng cả editing view data view (getData)
422
- conversion.for('downcast').add(dispatcher => {
423
- dispatcher.on('attribute:bindingValue:variable', (evt, data, conversionApi) => {
418
+ // 2b. bindingValue view
419
+ // - editingDowncast: HTML bind dùng createRawElement để hiển thị table/section trong editor.
420
+ // - dataDowncast (getData): CHỈ lưu data-binding-value (URI), không chèn block HTML vào <span> —
421
+ // tránh HTML lưu dạng <p><span>…<table>… (invalid / upcast CKEditor lỗi null.start).
422
+ const applyBindingValueAttributeToView = (isDataPipeline) => {
423
+ return (evt, data, conversionApi) => {
424
424
  if (!conversionApi.consumable.consume(data.item, evt.name))
425
425
  return;
426
426
  const viewWriter = conversionApi.writer;
@@ -430,18 +430,46 @@ class VariablePlugin extends Plugin {
430
430
  const bindingValue = data.attributeNewValue;
431
431
  const isBound = !!bindingValue;
432
432
  const display = data.item.getAttribute('display');
433
- // Cập nhật data-binding attribute trên container span
434
- viewWriter.setAttribute('data-binding', isBound ? 'true' : 'false', viewElement);
435
- // Xóa text node cũ và chèn text mới (bound value hoặc {{display}})
436
433
  viewWriter.remove(viewWriter.createRangeIn(viewElement));
437
- viewWriter.insert(viewWriter.createPositionAt(viewElement, 0), viewWriter.createText(isBound ? bindingValue : `{{${display}}}`));
438
- });
434
+ if (!isBound) {
435
+ viewWriter.setAttribute('data-binding', 'false', viewElement);
436
+ viewWriter.removeAttribute('data-binding-value', viewElement);
437
+ viewWriter.insert(viewWriter.createPositionAt(viewElement, 0), viewWriter.createText(`{{${display}}}`));
438
+ return;
439
+ }
440
+ const raw = bindingValue;
441
+ const isHtml = /<[a-z][\s\S]*>/i.test(raw);
442
+ if (isHtml) {
443
+ viewWriter.setAttribute('data-binding', 'html', viewElement);
444
+ viewWriter.setAttribute('data-binding-value', encodeURIComponent(raw), viewElement);
445
+ if (isDataPipeline) {
446
+ // Không nhét DOM con vào serialized HTML; upcast đọc binding từ data-binding-value.
447
+ return;
448
+ }
449
+ const htmlHost = viewWriter.createRawElement('span', { class: 'variable-html-content' }, (domElement) => {
450
+ domElement.innerHTML = raw;
451
+ });
452
+ viewWriter.insert(viewWriter.createPositionAt(viewElement, 0), htmlHost);
453
+ }
454
+ else {
455
+ viewWriter.setAttribute('data-binding', 'true', viewElement);
456
+ viewWriter.removeAttribute('data-binding-value', viewElement);
457
+ viewWriter.insert(viewWriter.createPositionAt(viewElement, 0), viewWriter.createText(raw));
458
+ }
459
+ };
460
+ };
461
+ conversion.for('dataDowncast').add(dispatcher => {
462
+ dispatcher.on('attribute:bindingValue:variable', applyBindingValueAttributeToView(true));
463
+ });
464
+ conversion.for('editingDowncast').add(dispatcher => {
465
+ dispatcher.on('attribute:bindingValue:variable', applyBindingValueAttributeToView(false));
439
466
  });
440
467
  // 3. HTML -> Model
441
468
  conversion.for('upcast').elementToElement({
442
469
  // NOTE: Chỉ khai báo các attribute CẦN THIẾT để nhận biết variable widget.
443
470
  // - data-binding KHÔNG được đưa vào required attributes (HTML cũ không có sẽ không được nhận biết).
444
- // - Nếu data-binding="true" trong HTML → đọc text content làm bindingValue để restore trạng thái bound.
471
+ // - data-binding="true" → đọc text child làm bindingValue (plain text).
472
+ // - data-binding="html" → đọc data-binding-value (URI-encoded) làm bindingValue.
445
473
  view: {
446
474
  name: 'span',
447
475
  classes: 'variable-widget ck-widget',
@@ -453,10 +481,21 @@ class VariablePlugin extends Plugin {
453
481
  },
454
482
  },
455
483
  model: (viewElement, { writer: modelWriter }) => {
456
- const isBound = viewElement.getAttribute('data-binding') === 'true';
484
+ const bindingMode = viewElement.getAttribute('data-binding');
457
485
  let bindingValue;
458
- if (isBound) {
459
- // Đọc text content làm bindingValue (bound value được lưu trực tiếp vào innerHTML)
486
+ if (bindingMode === 'html') {
487
+ const encoded = viewElement.getAttribute('data-binding-value');
488
+ if (encoded) {
489
+ try {
490
+ bindingValue = decodeURIComponent(encoded);
491
+ }
492
+ catch {
493
+ bindingValue = undefined;
494
+ }
495
+ }
496
+ }
497
+ else if (bindingMode === 'true') {
498
+ // Đọc text content làm bindingValue (bound value được lưu trực tiếp vào inner text)
460
499
  for (const child of viewElement.getChildren()) {
461
500
  if (child.is('$text')) {
462
501
  bindingValue = child.data;
@@ -991,6 +1030,32 @@ class VariablePlugin extends Plugin {
991
1030
  this.clearValues();
992
1031
  }
993
1032
  }
1033
+ /**
1034
+ * HTML từ getData() phiên bản cũ có thể chứa block (vd. table) bên trong `span.variable-widget[data-binding="html"]`,
1035
+ * khiến setData/upcast CKEditor lỗi (unexpected-error, null.start). Gọi trước `setData` để giữ chỉ
1036
+ * `data-binding-value` và bỏ các node con.
1037
+ */
1038
+ function sanitizeVariableHtmlBoundSerializedHtml(html) {
1039
+ if (!html || !html.includes('variable-widget'))
1040
+ return html;
1041
+ try {
1042
+ const doc = new DOMParser().parseFromString(`<div id="__sd_var_sanitize_root__">${html}</div>`, 'text/html');
1043
+ const root = doc.getElementById('__sd_var_sanitize_root__');
1044
+ if (!root)
1045
+ return html;
1046
+ root.querySelectorAll('span.variable-widget').forEach(el => {
1047
+ if (el.getAttribute('data-binding') !== 'html')
1048
+ return;
1049
+ if (!el.hasAttribute('data-binding-value'))
1050
+ return;
1051
+ el.replaceChildren();
1052
+ });
1053
+ return root.innerHTML;
1054
+ }
1055
+ catch {
1056
+ return html;
1057
+ }
1058
+ }
994
1059
 
995
1060
  class TableCustom extends Plugin {
996
1061
  init() {
@@ -4575,7 +4640,8 @@ class SdDocumentBuilder {
4575
4640
  }
4576
4641
  }
4577
4642
  setContent = (html) => {
4578
- this.#editor?.setData?.(html);
4643
+ const safe = sanitizeVariableHtmlBoundSerializedHtml(html);
4644
+ this.#editor?.setData?.(safe);
4579
4645
  };
4580
4646
  getContent = () => {
4581
4647
  if (this.#editor) {
@@ -4936,11 +5002,11 @@ class SdDocumentBuilder {
4936
5002
  });
4937
5003
  };
4938
5004
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdDocumentBuilder, deps: [], target: i0.ɵɵFactoryTarget.Component });
4939
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.21", type: SdDocumentBuilder, isStandalone: true, selector: "sd-document-builder", inputs: { option: "option", _disabled: ["disabled", "_disabled"] }, outputs: { contentChange: "contentChange" }, ngImport: i0, template: "<div class=\"builder-container\">\r\n <ckeditor class=\"w-full\" [editor]=\"Editor\" [config]=\"config\" (ready)=\"onReady($event)\" [disabled]=\"disabled\"> </ckeditor>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.builder-container{height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;align-items:center}:host{display:inline-block}:host ::ng-deep .ck-editor{display:flex;flex-direction:column;align-items:center;width:100%;--ck-content-font-family: \"Times New Roman\", serif !important;--ck-content-font-size: 13pt;--ck-content-line-height: 1.15}:host ::ng-deep .ck-editor__top,:host ::ng-deep .ck-editor__main{border:none!important;box-shadow:none!important}:host ::ng-deep .ck-editor__top{position:sticky;top:0;z-index:100;width:100%;min-width:600px;margin-bottom:10px}:host ::ng-deep .ck-editor__top .ck-sticky-panel__content{border:none!important}:host ::ng-deep .ck-editor__top .ck-toolbar{background:#fff!important;box-shadow:none!important;border-radius:8px;padding:8px!important}:host ::ng-deep .ck-editor__top .ck-toolbar .ck-toolbar__items{display:flex;justify-content:center;flex-wrap:wrap;align-items:center}:host ::ng-deep .ck-content{background-color:#fff;width:210mm;min-height:1123px;padding:20mm!important;box-sizing:border-box!important;box-shadow:0 10px 15px -3px #0000001a}:host ::ng-deep .ck-content h1,:host ::ng-deep .ck-content h2,:host ::ng-deep .ck-content h3,:host ::ng-deep .ck-content h4,:host ::ng-deep .ck-content h5,:host ::ng-deep .ck-content h6{font-weight:400;font-size:inherit;line-height:inherit;margin-bottom:4px}:host ::ng-deep .ck-content.landscape{width:297mm}:host ::ng-deep .ck-content>*{max-width:100%!important;box-sizing:border-box!important}:host ::ng-deep .ck-content p{margin-bottom:4px;text-indent:0}:host ::ng-deep .ck-content ul,:host ::ng-deep .ck-content ol{padding-left:20px!important;margin-left:0!important;margin-bottom:4px}:host ::ng-deep .ck-content li{margin-bottom:0}:host ::ng-deep .ck-content table{margin-bottom:4px}\n", ":host ::ng-deep .ck-heading-highlight{background-color:#fef08a}\n", "@charset \"UTF-8\";::ng-deep .ck-clipboard-drop-target-line{display:none!important}:host ::ng-deep .variable-widget{display:inline-block;-webkit-user-select:none;user-select:none}:host ::ng-deep .variable-widget:not([data-binding=true]){background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9!important;border-radius:4px;padding:2px 6px;font-size:0;margin:0 2px;vertical-align:middle;font-weight:600;cursor:default}:host ::ng-deep .variable-widget:not([data-binding=true]):before{content:attr(data-display);font-size:10px;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}:host ::ng-deep .variable-widget:not([data-binding=true]):hover{background-color:#bbdefb;box-shadow:0 1px 2px #0000001a}:host ::ng-deep .variable-widget:not([data-binding=true]).ck-widget_selected{outline:2px solid #2196f3;background-color:#bbdefb}:host ::ng-deep .variable-widget[data-binding=true]{background-color:#4caf501f;color:#2e7d32;border-bottom:2px solid #4caf50!important;border-radius:0;padding:0 1px;font-size:inherit;cursor:default}:host ::ng-deep .variable-widget[data-binding=true]:before{content:none}:host ::ng-deep .variable-widget[data-binding=true]:hover{background-color:#4caf5038}:host ::ng-deep .variable-widget[data-binding=true].ck-widget_selected{outline:2px solid #4caf50;background-color:#4caf5047}:host ::ng-deep .ck.ck-content .ck-widget,:host ::ng-deep .ck.ck-content .ck-widget:hover,:host ::ng-deep .ck.ck-content .ck-widget:focus,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected:hover{outline:none!important;box-shadow:none!important}\n", ":host ::ng-deep .highlight-range{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}\n", ":host ::ng-deep .ck-comment-marker{background-color:var(--comment-bg, rgba(59, 130, 246, .3));border-bottom:2px solid rgb(59,130,246);cursor:pointer;transition:all .2s ease}:host ::ng-deep .ck-comment-marker:hover{opacity:.8}:host ::ng-deep .ck-comment-marker.ck-comment-pending{background-color:var(--comment-pending-bg, rgba(245, 158, 11, .4));border-bottom-color:#f59e0b}:host ::ng-deep .ck-comment-marker.ck-comment-normal{background-color:var(--comment-bg, rgba(59, 130, 246, .2));border-bottom-color:#3b82f6}:host ::ng-deep .ck-comment-marker.ck-comment-selected{background-color:var(--comment-selected-bg, 59, 130, 246, .5);border-bottom-color:#3b82f6}:host ::ng-deep .ck-comment-marker.ck-comment-modified{background-color:var(--comment-modified-bg, rgba(249, 115, 22, .3));border-bottom-color:#f97316}:host ::ng-deep .ck-comment-marker.ck-comment-broken{background-color:var(--comment-broken-bg, rgba(239, 68, 68, .3));border-bottom-color:#ef4444;text-decoration:line-through;text-decoration-color:#ef4444}:host ::ng-deep .ck-balloon-panel .ck-button.ck-on:hover,:host ::ng-deep .ck-balloon-panel .ck-button.ck-off:hover{background:var(--ck-color-button-default-hover-background)}\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"] }] });
5005
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.21", type: SdDocumentBuilder, isStandalone: true, selector: "sd-document-builder", inputs: { option: "option", _disabled: ["disabled", "_disabled"] }, outputs: { contentChange: "contentChange" }, ngImport: i0, template: "<div class=\"builder-container\">\r\n <ckeditor class=\"w-full\" [editor]=\"Editor\" [config]=\"config\" (ready)=\"onReady($event)\" [disabled]=\"disabled\"> </ckeditor>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.builder-container{height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;align-items:center}:host{display:inline-block}:host ::ng-deep .ck-editor{display:flex;flex-direction:column;align-items:center;width:100%;--ck-content-font-family: \"Times New Roman\", serif !important;--ck-content-font-size: 13pt;--ck-content-line-height: 1.15}:host ::ng-deep .ck-editor__top,:host ::ng-deep .ck-editor__main{border:none!important;box-shadow:none!important}:host ::ng-deep .ck-editor__top{position:sticky;top:0;z-index:100;width:100%;min-width:600px;margin-bottom:10px}:host ::ng-deep .ck-editor__top .ck-sticky-panel__content{border:none!important}:host ::ng-deep .ck-editor__top .ck-toolbar{background:#fff!important;box-shadow:none!important;border-radius:8px;padding:8px!important}:host ::ng-deep .ck-editor__top .ck-toolbar .ck-toolbar__items{display:flex;justify-content:center;flex-wrap:wrap;align-items:center}:host ::ng-deep .ck-content{background-color:#fff;width:210mm;min-height:1123px;padding:20mm!important;box-sizing:border-box!important;box-shadow:0 10px 15px -3px #0000001a}:host ::ng-deep .ck-content h1,:host ::ng-deep .ck-content h2,:host ::ng-deep .ck-content h3,:host ::ng-deep .ck-content h4,:host ::ng-deep .ck-content h5,:host ::ng-deep .ck-content h6{font-weight:400;font-size:inherit;line-height:inherit;margin-bottom:4px}:host ::ng-deep .ck-content.landscape{width:297mm}:host ::ng-deep .ck-content>*{max-width:100%!important;box-sizing:border-box!important}:host ::ng-deep .ck-content p{margin-bottom:4px;text-indent:0}:host ::ng-deep .ck-content ul,:host ::ng-deep .ck-content ol{padding-left:20px!important;margin-left:0!important;margin-bottom:4px}:host ::ng-deep .ck-content li{margin-bottom:0}:host ::ng-deep .ck-content table{margin-bottom:4px}\n", ":host ::ng-deep .ck-heading-highlight{background-color:#fef08a}\n", "@charset \"UTF-8\";::ng-deep .ck-clipboard-drop-target-line{display:none!important}:host ::ng-deep .variable-widget{display:inline-block;-webkit-user-select:none;user-select:none}:host ::ng-deep .variable-widget:not([data-binding=true]):not([data-binding=html]){background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9!important;border-radius:4px;padding:2px 6px;font-size:0;margin:0 2px;vertical-align:middle;font-weight:600;cursor:default}:host ::ng-deep .variable-widget:not([data-binding=true]):not([data-binding=html]):before{content:attr(data-display);font-size:10px;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}:host ::ng-deep .variable-widget:not([data-binding=true]):not([data-binding=html]):hover{background-color:#bbdefb;box-shadow:0 1px 2px #0000001a}:host ::ng-deep .variable-widget:not([data-binding=true]):not([data-binding=html]).ck-widget_selected{outline:2px solid #2196f3;background-color:#bbdefb}:host ::ng-deep .variable-widget[data-binding=true]{background-color:#4caf501f;color:#2e7d32;border-bottom:2px solid #4caf50!important;border-radius:0;padding:0 1px;font-size:inherit;cursor:default}:host ::ng-deep .variable-widget[data-binding=true]:before{content:none}:host ::ng-deep .variable-widget[data-binding=true]:hover{background-color:#4caf5038}:host ::ng-deep .variable-widget[data-binding=true].ck-widget_selected{outline:2px solid #4caf50;background-color:#4caf5047}:host ::ng-deep .variable-widget[data-binding=html]{display:block;background-color:transparent;border:1px dashed #4caf50!important;border-radius:4px;padding:4px;-webkit-user-select:text;user-select:text;pointer-events:none}:host ::ng-deep .variable-widget[data-binding=html]:before{content:none}:host ::ng-deep .variable-widget[data-binding=html]:hover{background-color:#4caf500f}:host ::ng-deep .variable-widget[data-binding=html].ck-widget_selected{outline:2px solid #4caf50;background-color:#4caf5014}:host ::ng-deep .variable-widget[data-binding=html] .variable-html-content{display:block}:host ::ng-deep .ck.ck-content .ck-widget,:host ::ng-deep .ck.ck-content .ck-widget:hover,:host ::ng-deep .ck.ck-content .ck-widget:focus,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected:hover{outline:none!important;box-shadow:none!important}\n", ":host ::ng-deep .highlight-range{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}\n", ":host ::ng-deep .ck-comment-marker{background-color:var(--comment-bg, rgba(59, 130, 246, .3));border-bottom:2px solid rgb(59,130,246);cursor:pointer;transition:all .2s ease}:host ::ng-deep .ck-comment-marker:hover{opacity:.8}:host ::ng-deep .ck-comment-marker.ck-comment-pending{background-color:var(--comment-pending-bg, rgba(245, 158, 11, .4));border-bottom-color:#f59e0b}:host ::ng-deep .ck-comment-marker.ck-comment-normal{background-color:var(--comment-bg, rgba(59, 130, 246, .2));border-bottom-color:#3b82f6}:host ::ng-deep .ck-comment-marker.ck-comment-selected{background-color:var(--comment-selected-bg, 59, 130, 246, .5);border-bottom-color:#3b82f6}:host ::ng-deep .ck-comment-marker.ck-comment-modified{background-color:var(--comment-modified-bg, rgba(249, 115, 22, .3));border-bottom-color:#f97316}:host ::ng-deep .ck-comment-marker.ck-comment-broken{background-color:var(--comment-broken-bg, rgba(239, 68, 68, .3));border-bottom-color:#ef4444;text-decoration:line-through;text-decoration-color:#ef4444}:host ::ng-deep .ck-balloon-panel .ck-button.ck-on:hover,:host ::ng-deep .ck-balloon-panel .ck-button.ck-off:hover{background:var(--ck-color-button-default-hover-background)}\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"] }] });
4940
5006
  }
4941
5007
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.21", ngImport: i0, type: SdDocumentBuilder, decorators: [{
4942
5008
  type: Component,
4943
- args: [{ selector: 'sd-document-builder', standalone: true, imports: [CommonModule, CKEditorModule], template: "<div class=\"builder-container\">\r\n <ckeditor class=\"w-full\" [editor]=\"Editor\" [config]=\"config\" (ready)=\"onReady($event)\" [disabled]=\"disabled\"> </ckeditor>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.builder-container{height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;align-items:center}:host{display:inline-block}:host ::ng-deep .ck-editor{display:flex;flex-direction:column;align-items:center;width:100%;--ck-content-font-family: \"Times New Roman\", serif !important;--ck-content-font-size: 13pt;--ck-content-line-height: 1.15}:host ::ng-deep .ck-editor__top,:host ::ng-deep .ck-editor__main{border:none!important;box-shadow:none!important}:host ::ng-deep .ck-editor__top{position:sticky;top:0;z-index:100;width:100%;min-width:600px;margin-bottom:10px}:host ::ng-deep .ck-editor__top .ck-sticky-panel__content{border:none!important}:host ::ng-deep .ck-editor__top .ck-toolbar{background:#fff!important;box-shadow:none!important;border-radius:8px;padding:8px!important}:host ::ng-deep .ck-editor__top .ck-toolbar .ck-toolbar__items{display:flex;justify-content:center;flex-wrap:wrap;align-items:center}:host ::ng-deep .ck-content{background-color:#fff;width:210mm;min-height:1123px;padding:20mm!important;box-sizing:border-box!important;box-shadow:0 10px 15px -3px #0000001a}:host ::ng-deep .ck-content h1,:host ::ng-deep .ck-content h2,:host ::ng-deep .ck-content h3,:host ::ng-deep .ck-content h4,:host ::ng-deep .ck-content h5,:host ::ng-deep .ck-content h6{font-weight:400;font-size:inherit;line-height:inherit;margin-bottom:4px}:host ::ng-deep .ck-content.landscape{width:297mm}:host ::ng-deep .ck-content>*{max-width:100%!important;box-sizing:border-box!important}:host ::ng-deep .ck-content p{margin-bottom:4px;text-indent:0}:host ::ng-deep .ck-content ul,:host ::ng-deep .ck-content ol{padding-left:20px!important;margin-left:0!important;margin-bottom:4px}:host ::ng-deep .ck-content li{margin-bottom:0}:host ::ng-deep .ck-content table{margin-bottom:4px}\n", ":host ::ng-deep .ck-heading-highlight{background-color:#fef08a}\n", "@charset \"UTF-8\";::ng-deep .ck-clipboard-drop-target-line{display:none!important}:host ::ng-deep .variable-widget{display:inline-block;-webkit-user-select:none;user-select:none}:host ::ng-deep .variable-widget:not([data-binding=true]){background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9!important;border-radius:4px;padding:2px 6px;font-size:0;margin:0 2px;vertical-align:middle;font-weight:600;cursor:default}:host ::ng-deep .variable-widget:not([data-binding=true]):before{content:attr(data-display);font-size:10px;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}:host ::ng-deep .variable-widget:not([data-binding=true]):hover{background-color:#bbdefb;box-shadow:0 1px 2px #0000001a}:host ::ng-deep .variable-widget:not([data-binding=true]).ck-widget_selected{outline:2px solid #2196f3;background-color:#bbdefb}:host ::ng-deep .variable-widget[data-binding=true]{background-color:#4caf501f;color:#2e7d32;border-bottom:2px solid #4caf50!important;border-radius:0;padding:0 1px;font-size:inherit;cursor:default}:host ::ng-deep .variable-widget[data-binding=true]:before{content:none}:host ::ng-deep .variable-widget[data-binding=true]:hover{background-color:#4caf5038}:host ::ng-deep .variable-widget[data-binding=true].ck-widget_selected{outline:2px solid #4caf50;background-color:#4caf5047}:host ::ng-deep .ck.ck-content .ck-widget,:host ::ng-deep .ck.ck-content .ck-widget:hover,:host ::ng-deep .ck.ck-content .ck-widget:focus,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected:hover{outline:none!important;box-shadow:none!important}\n", ":host ::ng-deep .highlight-range{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}\n", ":host ::ng-deep .ck-comment-marker{background-color:var(--comment-bg, rgba(59, 130, 246, .3));border-bottom:2px solid rgb(59,130,246);cursor:pointer;transition:all .2s ease}:host ::ng-deep .ck-comment-marker:hover{opacity:.8}:host ::ng-deep .ck-comment-marker.ck-comment-pending{background-color:var(--comment-pending-bg, rgba(245, 158, 11, .4));border-bottom-color:#f59e0b}:host ::ng-deep .ck-comment-marker.ck-comment-normal{background-color:var(--comment-bg, rgba(59, 130, 246, .2));border-bottom-color:#3b82f6}:host ::ng-deep .ck-comment-marker.ck-comment-selected{background-color:var(--comment-selected-bg, 59, 130, 246, .5);border-bottom-color:#3b82f6}:host ::ng-deep .ck-comment-marker.ck-comment-modified{background-color:var(--comment-modified-bg, rgba(249, 115, 22, .3));border-bottom-color:#f97316}:host ::ng-deep .ck-comment-marker.ck-comment-broken{background-color:var(--comment-broken-bg, rgba(239, 68, 68, .3));border-bottom-color:#ef4444;text-decoration:line-through;text-decoration-color:#ef4444}:host ::ng-deep .ck-balloon-panel .ck-button.ck-on:hover,:host ::ng-deep .ck-balloon-panel .ck-button.ck-off:hover{background:var(--ck-color-button-default-hover-background)}\n"] }]
5009
+ args: [{ selector: 'sd-document-builder', standalone: true, imports: [CommonModule, CKEditorModule], template: "<div class=\"builder-container\">\r\n <ckeditor class=\"w-full\" [editor]=\"Editor\" [config]=\"config\" (ready)=\"onReady($event)\" [disabled]=\"disabled\"> </ckeditor>\r\n</div>\r\n", styles: ["@charset \"UTF-8\";.builder-container{height:100%;overflow-y:auto;width:100%;display:flex;flex-direction:column;align-items:center}:host{display:inline-block}:host ::ng-deep .ck-editor{display:flex;flex-direction:column;align-items:center;width:100%;--ck-content-font-family: \"Times New Roman\", serif !important;--ck-content-font-size: 13pt;--ck-content-line-height: 1.15}:host ::ng-deep .ck-editor__top,:host ::ng-deep .ck-editor__main{border:none!important;box-shadow:none!important}:host ::ng-deep .ck-editor__top{position:sticky;top:0;z-index:100;width:100%;min-width:600px;margin-bottom:10px}:host ::ng-deep .ck-editor__top .ck-sticky-panel__content{border:none!important}:host ::ng-deep .ck-editor__top .ck-toolbar{background:#fff!important;box-shadow:none!important;border-radius:8px;padding:8px!important}:host ::ng-deep .ck-editor__top .ck-toolbar .ck-toolbar__items{display:flex;justify-content:center;flex-wrap:wrap;align-items:center}:host ::ng-deep .ck-content{background-color:#fff;width:210mm;min-height:1123px;padding:20mm!important;box-sizing:border-box!important;box-shadow:0 10px 15px -3px #0000001a}:host ::ng-deep .ck-content h1,:host ::ng-deep .ck-content h2,:host ::ng-deep .ck-content h3,:host ::ng-deep .ck-content h4,:host ::ng-deep .ck-content h5,:host ::ng-deep .ck-content h6{font-weight:400;font-size:inherit;line-height:inherit;margin-bottom:4px}:host ::ng-deep .ck-content.landscape{width:297mm}:host ::ng-deep .ck-content>*{max-width:100%!important;box-sizing:border-box!important}:host ::ng-deep .ck-content p{margin-bottom:4px;text-indent:0}:host ::ng-deep .ck-content ul,:host ::ng-deep .ck-content ol{padding-left:20px!important;margin-left:0!important;margin-bottom:4px}:host ::ng-deep .ck-content li{margin-bottom:0}:host ::ng-deep .ck-content table{margin-bottom:4px}\n", ":host ::ng-deep .ck-heading-highlight{background-color:#fef08a}\n", "@charset \"UTF-8\";::ng-deep .ck-clipboard-drop-target-line{display:none!important}:host ::ng-deep .variable-widget{display:inline-block;-webkit-user-select:none;user-select:none}:host ::ng-deep .variable-widget:not([data-binding=true]):not([data-binding=html]){background-color:#e3f2fd;color:#1976d2;border:1px solid #90caf9!important;border-radius:4px;padding:2px 6px;font-size:0;margin:0 2px;vertical-align:middle;font-weight:600;cursor:default}:host ::ng-deep .variable-widget:not([data-binding=true]):not([data-binding=html]):before{content:attr(data-display);font-size:10px;font-family:Segoe UI,Tahoma,Geneva,Verdana,sans-serif}:host ::ng-deep .variable-widget:not([data-binding=true]):not([data-binding=html]):hover{background-color:#bbdefb;box-shadow:0 1px 2px #0000001a}:host ::ng-deep .variable-widget:not([data-binding=true]):not([data-binding=html]).ck-widget_selected{outline:2px solid #2196f3;background-color:#bbdefb}:host ::ng-deep .variable-widget[data-binding=true]{background-color:#4caf501f;color:#2e7d32;border-bottom:2px solid #4caf50!important;border-radius:0;padding:0 1px;font-size:inherit;cursor:default}:host ::ng-deep .variable-widget[data-binding=true]:before{content:none}:host ::ng-deep .variable-widget[data-binding=true]:hover{background-color:#4caf5038}:host ::ng-deep .variable-widget[data-binding=true].ck-widget_selected{outline:2px solid #4caf50;background-color:#4caf5047}:host ::ng-deep .variable-widget[data-binding=html]{display:block;background-color:transparent;border:1px dashed #4caf50!important;border-radius:4px;padding:4px;-webkit-user-select:text;user-select:text;pointer-events:none}:host ::ng-deep .variable-widget[data-binding=html]:before{content:none}:host ::ng-deep .variable-widget[data-binding=html]:hover{background-color:#4caf500f}:host ::ng-deep .variable-widget[data-binding=html].ck-widget_selected{outline:2px solid #4caf50;background-color:#4caf5014}:host ::ng-deep .variable-widget[data-binding=html] .variable-html-content{display:block}:host ::ng-deep .ck.ck-content .ck-widget,:host ::ng-deep .ck.ck-content .ck-widget:hover,:host ::ng-deep .ck.ck-content .ck-widget:focus,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected,:host ::ng-deep .ck.ck-content .ck-widget.ck-widget_selected:hover{outline:none!important;box-shadow:none!important}\n", ":host ::ng-deep .highlight-range{background-color:#ffeb3b80;border-bottom:2px solid #fbc02d;transition:background-color .2s;cursor:pointer}\n", ":host ::ng-deep .ck-comment-marker{background-color:var(--comment-bg, rgba(59, 130, 246, .3));border-bottom:2px solid rgb(59,130,246);cursor:pointer;transition:all .2s ease}:host ::ng-deep .ck-comment-marker:hover{opacity:.8}:host ::ng-deep .ck-comment-marker.ck-comment-pending{background-color:var(--comment-pending-bg, rgba(245, 158, 11, .4));border-bottom-color:#f59e0b}:host ::ng-deep .ck-comment-marker.ck-comment-normal{background-color:var(--comment-bg, rgba(59, 130, 246, .2));border-bottom-color:#3b82f6}:host ::ng-deep .ck-comment-marker.ck-comment-selected{background-color:var(--comment-selected-bg, 59, 130, 246, .5);border-bottom-color:#3b82f6}:host ::ng-deep .ck-comment-marker.ck-comment-modified{background-color:var(--comment-modified-bg, rgba(249, 115, 22, .3));border-bottom-color:#f97316}:host ::ng-deep .ck-comment-marker.ck-comment-broken{background-color:var(--comment-broken-bg, rgba(239, 68, 68, .3));border-bottom-color:#ef4444;text-decoration:line-through;text-decoration-color:#ef4444}:host ::ng-deep .ck-balloon-panel .ck-button.ck-on:hover,:host ::ng-deep .ck-balloon-panel .ck-button.ck-off:hover{background:var(--ck-color-button-default-hover-background)}\n"] }]
4944
5010
  }], propDecorators: { option: [{
4945
5011
  type: Input,
4946
5012
  args: [{ required: true }]