@phuong-tran-redoc/document-engine-core 0.1.7 → 0.1.8
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/README.md +5 -0
- package/index.esm.js +37 -3
- package/package.json +1 -1
- package/src/compat/ckeditor/ck-html.d.ts +3 -2
package/README.md
CHANGED
|
@@ -112,6 +112,11 @@ const saved = toCkHtml(editor.getHTML(), { wrapperClass: wrapperClass ?? false }
|
|
|
112
112
|
verifyCkRoundTrip(storedHtml, saved); // before editing: is the document reproduced exactly?
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
CKEditor restricted-editing exceptions (`<span class="restricted-editing-exception">`) load as editable
|
|
116
|
+
regions when `EditableRegion` and `RestrictedEditing` are registered, and are saved back in that markup. In
|
|
117
|
+
restricted mode, set content with `editor.chain().setMeta('restrictedEditing', { allow: true }).setContent(html).run()`
|
|
118
|
+
— otherwise the restriction blocks the change once the document is not empty.
|
|
119
|
+
|
|
115
120
|
On a backend (no DOM) pass a parser. `createCkDomParser()` uses happy-dom, which `@tiptap/html` already installs:
|
|
116
121
|
|
|
117
122
|
```typescript
|
package/index.esm.js
CHANGED
|
@@ -61,8 +61,8 @@ function absoluteLengthToPx(value) {
|
|
|
61
61
|
* `data-ck` attribute that the {@link CkCompat} extension carries through editing.
|
|
62
62
|
* - {@link toCkHtml} runs on `editor.getHTML()`. It rebuilds the CKEditor markup from those
|
|
63
63
|
* records: attribute order, `prop:value;` style formatting, `<figure class="table">`,
|
|
64
|
-
* `<colgroup>`, bare text in single-paragraph cells, `<i>`, page breaks, dynamic fields
|
|
65
|
-
* the wrapper. Anything the user changed is taken from the editor; anything the editor does
|
|
64
|
+
* `<colgroup>`, bare text in single-paragraph cells, `<i>`, page breaks, dynamic fields,
|
|
65
|
+
* restricted-editing exceptions and the wrapper. Anything the user changed is taken from the editor; anything the editor does
|
|
66
66
|
* not model is kept verbatim.
|
|
67
67
|
*
|
|
68
68
|
* Both functions need a `DOMParser`. In the browser the global one is used. In Node (no global DOM)
|
|
@@ -87,6 +87,7 @@ const CK_UNSUPPORTED_CONTENT = {
|
|
|
87
87
|
dynamicImage: '.redr-dynamic-image',
|
|
88
88
|
dealTable: '.redr-deal-table',
|
|
89
89
|
editorColumn: '.redr-editor-column',
|
|
90
|
+
/** @deprecated Supported since 0.1.8 (loads as an editable region); no longer reported. */
|
|
90
91
|
restrictedEditingException: '.restricted-editing-exception',
|
|
91
92
|
image: 'img, figure.image',
|
|
92
93
|
};
|
|
@@ -122,6 +123,8 @@ const DEFAULT_CELL_VERTICAL_ALIGN = 'middle';
|
|
|
122
123
|
const NEUTRAL_DECLARATIONS = new Set(['margin-left:0px', 'margin-left:0', 'margin-left:nullpx']);
|
|
123
124
|
const DYNAMIC_FIELD_BASE_CLASSES = ['red-dynamic-field', 'inline-field', 'redr-handlebar-field'];
|
|
124
125
|
const DYNAMIC_FIELD_HAS_VALUE_CLASS = 'red-dynamic-field--has-value';
|
|
126
|
+
/** Markup of CKEditor's restricted-editing exception (an editable region). */
|
|
127
|
+
const EDITABLE_REGION_CK_CLASS = 'restricted-editing-exception';
|
|
125
128
|
const PAGE_BREAK_HTML = '<div class="page-break" style="page-break-after:always;"><span style="display:none;"> </span></div>';
|
|
126
129
|
function parse(html, domParser) {
|
|
127
130
|
if (!domParser && typeof DOMParser === 'undefined') {
|
|
@@ -257,7 +260,7 @@ function fromCkHtml(html, options = {}) {
|
|
|
257
260
|
origin.c = Array.from(cols).map(attrsOf);
|
|
258
261
|
writeOrigin(table, origin);
|
|
259
262
|
});
|
|
260
|
-
const unsupported = Object.keys(CK_UNSUPPORTED_CONTENT).filter((key) => body.querySelector(CK_UNSUPPORTED_CONTENT[key]));
|
|
263
|
+
const unsupported = Object.keys(CK_UNSUPPORTED_CONTENT).filter((key) => key !== 'restrictedEditingException' && body.querySelector(CK_UNSUPPORTED_CONTENT[key]));
|
|
261
264
|
return { html: body.innerHTML, wrapperClass, unsupported };
|
|
262
265
|
}
|
|
263
266
|
/**
|
|
@@ -480,6 +483,33 @@ function exportDynamicFields(body) {
|
|
|
480
483
|
el.replaceWith(span);
|
|
481
484
|
});
|
|
482
485
|
}
|
|
486
|
+
/**
|
|
487
|
+
* Write editable regions as CKEditor restricted-editing exceptions. The editor fills a region it
|
|
488
|
+
* creates empty with a zero-width space (and refills one emptied in restricted mode with a space);
|
|
489
|
+
* a region left without text is written as ` ` so it stays in the document (CKEditor's
|
|
490
|
+
* empty-content convention). The exception class is always kept, whatever the record says: it is
|
|
491
|
+
* what identifies the region in stored HTML.
|
|
492
|
+
*/
|
|
493
|
+
function exportEditableRegions(body) {
|
|
494
|
+
body.querySelectorAll('span[data-editable-region]').forEach((el) => {
|
|
495
|
+
var _a, _b, _c;
|
|
496
|
+
const span = el.ownerDocument.createElement('span');
|
|
497
|
+
setAttributesInOrder(span, (_b = (_a = readOrigin(el)) === null || _a === void 0 ? void 0 : _a.a) !== null && _b !== void 0 ? _b : [['class', EDITABLE_REGION_CK_CLASS]]);
|
|
498
|
+
if (!span.classList.contains(EDITABLE_REGION_CK_CLASS))
|
|
499
|
+
span.classList.add(EDITABLE_REGION_CK_CLASS);
|
|
500
|
+
span.append(...Array.from(el.childNodes));
|
|
501
|
+
stripZeroWidthSpaces(span);
|
|
502
|
+
if (!((_c = span.textContent) === null || _c === void 0 ? void 0 : _c.trim()))
|
|
503
|
+
span.textContent = '\u00a0';
|
|
504
|
+
el.replaceWith(span);
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
function stripZeroWidthSpaces(node) {
|
|
508
|
+
var _a;
|
|
509
|
+
if (node.nodeType === 3)
|
|
510
|
+
node.nodeValue = ((_a = node.nodeValue) !== null && _a !== void 0 ? _a : '').replace(/\u200b/g, '');
|
|
511
|
+
node.childNodes.forEach(stripZeroWidthSpaces);
|
|
512
|
+
}
|
|
483
513
|
/**
|
|
484
514
|
* Tiptap's `TrailingNode` appends an empty paragraph whenever a document ends in a table or other
|
|
485
515
|
* non-paragraph block. CKEditor allows that ending, so drop the paragraph unless the source had it.
|
|
@@ -555,12 +585,15 @@ function toCkHtml(html, options = {}) {
|
|
|
555
585
|
const probe = body.ownerDocument.createElement('span');
|
|
556
586
|
exportDynamicFields(body);
|
|
557
587
|
exportPageBreaks(body);
|
|
588
|
+
exportEditableRegions(body);
|
|
558
589
|
// Reconcile plain elements before tables restructure cells (so `<p>` attributes are final).
|
|
559
590
|
body.querySelectorAll('*').forEach((el) => {
|
|
560
591
|
var _a, _b;
|
|
561
592
|
const tag = el.tagName.toLowerCase();
|
|
562
593
|
if (tag === 'col' || el.closest('div.page-break') || el.matches('span[id^="red-dynamic-field"]'))
|
|
563
594
|
return;
|
|
595
|
+
if (el.matches(`span.${EDITABLE_REGION_CK_CLASS}`))
|
|
596
|
+
return;
|
|
564
597
|
reconcileElement(el, (_b = (_a = readOrigin(el)) === null || _a === void 0 ? void 0 : _a.a) !== null && _b !== void 0 ? _b : null, probe);
|
|
565
598
|
});
|
|
566
599
|
dropTrailingNode(body);
|
|
@@ -605,6 +638,7 @@ const CK_COMPAT_TYPES = [
|
|
|
605
638
|
'link',
|
|
606
639
|
'dynamicField',
|
|
607
640
|
'pageBreak',
|
|
641
|
+
'editableRegion',
|
|
608
642
|
];
|
|
609
643
|
const ckOrigin = (fallback) => ({
|
|
610
644
|
default: null,
|
package/package.json
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* `data-ck` attribute that the {@link CkCompat} extension carries through editing.
|
|
10
10
|
* - {@link toCkHtml} runs on `editor.getHTML()`. It rebuilds the CKEditor markup from those
|
|
11
11
|
* records: attribute order, `prop:value;` style formatting, `<figure class="table">`,
|
|
12
|
-
* `<colgroup>`, bare text in single-paragraph cells, `<i>`, page breaks, dynamic fields
|
|
13
|
-
* the wrapper. Anything the user changed is taken from the editor; anything the editor does
|
|
12
|
+
* `<colgroup>`, bare text in single-paragraph cells, `<i>`, page breaks, dynamic fields,
|
|
13
|
+
* restricted-editing exceptions and the wrapper. Anything the user changed is taken from the editor; anything the editor does
|
|
14
14
|
* not model is kept verbatim.
|
|
15
15
|
*
|
|
16
16
|
* Both functions need a `DOMParser`. In the browser the global one is used. In Node (no global DOM)
|
|
@@ -61,6 +61,7 @@ export declare const CK_UNSUPPORTED_CONTENT: {
|
|
|
61
61
|
readonly dynamicImage: ".redr-dynamic-image";
|
|
62
62
|
readonly dealTable: ".redr-deal-table";
|
|
63
63
|
readonly editorColumn: ".redr-editor-column";
|
|
64
|
+
/** @deprecated Supported since 0.1.8 (loads as an editable region); no longer reported. */
|
|
64
65
|
readonly restrictedEditingException: ".restricted-editing-exception";
|
|
65
66
|
readonly image: "img, figure.image";
|
|
66
67
|
};
|