@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.
- package/components/document-builder/src/document-builder.component.d.ts +7 -6
- package/components/document-builder/src/document-builder.model.d.ts +1 -0
- package/components/document-builder/src/plugins/heading/heading.plugin.d.ts +4 -0
- package/components/document-builder/src/plugins/{image-upload.plugin.d.ts → image-upload/image-upload.plugin.d.ts} +0 -4
- package/components/document-builder/src/plugins/index.d.ts +6 -5
- package/components/table/src/models/table-item.model.d.ts +2 -1
- package/components/table/src/models/table-option.model.d.ts +2 -1
- package/fesm2022/sd-angular-core-components-document-builder.mjs +240 -333
- package/fesm2022/sd-angular-core-components-document-builder.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-table.mjs +361 -73
- package/fesm2022/sd-angular-core-components-table.mjs.map +1 -1
- package/fesm2022/sd-angular-core-components-workflow.mjs +3 -3
- package/fesm2022/sd-angular-core-components-workflow.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-autocomplete.mjs +4 -2
- package/fesm2022/sd-angular-core-forms-autocomplete.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-date.mjs +4 -2
- package/fesm2022/sd-angular-core-forms-date.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-datetime.mjs +17 -3
- package/fesm2022/sd-angular-core-forms-datetime.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-input-number.mjs +4 -3
- package/fesm2022/sd-angular-core-forms-input-number.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-input.mjs +4 -2
- package/fesm2022/sd-angular-core-forms-input.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-radio.mjs +4 -2
- package/fesm2022/sd-angular-core-forms-radio.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-select.mjs +4 -2
- package/fesm2022/sd-angular-core-forms-select.mjs.map +1 -1
- package/fesm2022/sd-angular-core-forms-textarea.mjs +14 -2
- package/fesm2022/sd-angular-core-forms-textarea.mjs.map +1 -1
- package/fesm2022/sd-angular-core-pipes.mjs +21 -1
- package/fesm2022/sd-angular-core-pipes.mjs.map +1 -1
- package/forms/datetime/src/datetime.component.d.ts +4 -1
- package/package.json +69 -69
- package/pipes/index.d.ts +1 -0
- package/pipes/src/empty.pipe.d.ts +7 -0
- /package/components/document-builder/src/plugins/{comment.plugin.d.ts → comment/comment.plugin.d.ts} +0 -0
- /package/components/document-builder/src/plugins/{page-orientation.plugin.d.ts → page-orientation/page-orientation.plugin.d.ts} +0 -0
- /package/components/document-builder/src/plugins/{table-fit.plugin.d.ts → table-fit/table-fit.plugin.d.ts} +0 -0
- /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
|
-
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
167
|
+
// Ẩn button nếu không có 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,
|
|
179
|
+
isEnabled: false,
|
|
243
180
|
});
|
|
244
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
341
|
+
// 4.2. Đặt con trỏ ra sau biến
|
|
402
342
|
writer.setSelection(variableElem, 'after');
|
|
403
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
415
|
-
// Đôi khi View chưa kịp render lại, ta ép xóa class thủ công trên root
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
1197
|
-
|
|
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
|
-
|
|
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
|
-
},
|
|
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
|
-
//
|
|
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 LÝ COMMENT
|
|
1312
1229
|
// ========================================================================
|
|
1313
1230
|
comment = {
|
|
1314
1231
|
/**
|
|
@@ -1454,72 +1371,71 @@ class SdDocumentBuilder {
|
|
|
1454
1371
|
}
|
|
1455
1372
|
},
|
|
1456
1373
|
};
|
|
1457
|
-
//
|
|
1458
|
-
//
|
|
1459
|
-
//
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
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\"
|
|
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\"
|
|
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 }]
|