@ckeditor/ckeditor5-editor-multi-root 37.1.0 → 38.0.0-rc.0
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/build/editor-multi-root.js +1 -1
- package/package.json +16 -14
- package/src/multirooteditor.d.ts +49 -0
- package/src/multirooteditor.js +130 -4
@@ -1,4 +1,4 @@
|
|
1
1
|
/*!
|
2
2
|
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
|
3
3
|
* For licensing, see LICENSE.md.
|
4
|
-
*/(()=>{var t={704:(t,e,o)=>{t.exports=o(79)("./src/core.js")},492:(t,e,o)=>{t.exports=o(79)("./src/engine.js")},273:(t,e,o)=>{t.exports=o(79)("./src/ui.js")},209:(t,e,o)=>{t.exports=o(79)("./src/utils.js")},434:(t,e,o)=>{t.exports=o(79)("./src/watchdog.js")},79:t=>{"use strict";t.exports=CKEditor5.dll}},e={};function o(i){var
|
4
|
+
*/(()=>{var t={704:(t,e,o)=>{t.exports=o(79)("./src/core.js")},492:(t,e,o)=>{t.exports=o(79)("./src/engine.js")},273:(t,e,o)=>{t.exports=o(79)("./src/ui.js")},209:(t,e,o)=>{t.exports=o(79)("./src/utils.js")},434:(t,e,o)=>{t.exports=o(79)("./src/watchdog.js")},79:t=>{"use strict";t.exports=CKEditor5.dll}},e={};function o(i){var s=e[i];if(void 0!==s)return s.exports;var r=e[i]={exports:{}};return t[i](r,r.exports,o),r.exports}o.d=(t,e)=>{for(var i in e)o.o(e,i)&&!o.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})},o.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e),o.r=t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})};var i={};(()=>{"use strict";o.r(i),o.d(i,{MultiRootEditor:()=>W});var t=o(704),e=o(209),s=o(434),r=o(273),n=o(492);class a extends r.EditorUI{constructor(t,e){super(t),this.view=e,this._lastFocusedEditableElement=null}init(){this.view.render(),this.focusTracker.on("change:focusedElement",((t,e,o)=>{for(const t of Object.values(this.view.editables))o===t.element&&(this._lastFocusedEditableElement=t.element)})),this.focusTracker.on("change:isFocused",((t,e,o)=>{o||(this._lastFocusedEditableElement=null)}));for(const t of Object.values(this.view.editables))this.addEditable(t);this._initToolbar(),this.fire("ready")}addEditable(t,e){const o=t.element;this.editor.editing.view.attachDomRoot(o,t.name),this.setEditableElement(t.name,o),t.bind("isFocused").to(this.focusTracker,"isFocused",this.focusTracker,"focusedElement",((t,e)=>!!t&&(e===o||this._lastFocusedEditableElement===o))),this._initPlaceholder(t,e)}removeEditable(t){this.editor.editing.view.detachDomRoot(t.name),t.unbind("isFocused"),this.removeEditableElement(t.name)}destroy(){super.destroy();for(const t of Object.values(this.view.editables))this.removeEditable(t);this.view.destroy()}_initToolbar(){const t=this.editor,e=this.view;e.toolbar.fillFromConfig(t.config.get("toolbar"),this.componentFactory),this.addToolbar(e.toolbar)}_initPlaceholder(t,e){if(!e){const o=this.editor.config.get("placeholder");o&&(e="string"==typeof o?o:o[t.name])}if(!e)return;const o=this.editor.editing.view,i=o.document.getRoot(t.name);(0,n.enablePlaceholder)({view:o,element:i,text:e,isDirectHost:!1,keepOnFocus:!0})}}class c extends r.EditorUIView{constructor(t,e,o,i={}){super(t),this._editingView=e,this.toolbar=new r.ToolbarView(t,{shouldGroupWhenFull:i.shouldToolbarGroupWhenFull}),this.editables={};for(const t of o){const e=i.editableElements?i.editableElements[t]:void 0;this.createEditable(t,e)}this.editable=Object.values(this.editables)[0],this.toolbar.extendTemplate({attributes:{class:["ck-reset_all","ck-rounded-corners"],dir:t.uiLanguageDirection}})}createEditable(t,e){const o=this.locale.t,i=new r.InlineEditableUIView(this.locale,this._editingView,e,{label:t=>o("Rich Text Editor. Editing area: %0",t.name)});return this.editables[t]=i,i.name=t,this.isRendered&&this.registerChild(i),i}removeEditable(t){const e=this.editables[t];this.isRendered&&this.deregisterChild(e),delete this.editables[t],e.destroy()}render(){super.render(),this.registerChild(Object.values(this.editables)),this.registerChild(this.toolbar)}}const d=function(t){return null!=t&&"object"==typeof t};const l="object"==typeof global&&global&&global.Object===Object&&global;var h="object"==typeof self&&self&&self.Object===Object&&self;const u=(l||h||Function("return this")()).Symbol;var f=Object.prototype,b=f.hasOwnProperty,m=f.toString,g=u?u.toStringTag:void 0;const y=function(t){var e=b.call(t,g),o=t[g];try{t[g]=void 0;var i=!0}catch(t){}var s=m.call(t);return i&&(e?t[g]=o:delete t[g]),s};var v=Object.prototype.toString;const E=function(t){return v.call(t)};var p=u?u.toStringTag:void 0;const O=function(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":p&&p in Object(t)?y(t):E(t)};const w=function(t,e){return function(o){return t(e(o))}}(Object.getPrototypeOf,Object);var R=Function.prototype,j=Object.prototype,_=R.toString,C=j.hasOwnProperty,k=_.call(Object);const x=function(t){if(!d(t)||"[object Object]"!=O(t))return!1;var e=w(t);if(null===e)return!0;var o=C.call(e,"constructor")&&e.constructor;return"function"==typeof o&&o instanceof o&&_.call(o)==k};const D=function(t){return d(t)&&1===t.nodeType&&!x(t)};var T=Object.defineProperty,A=Object.defineProperties,F=Object.getOwnPropertyDescriptors,P=Object.getOwnPropertySymbols,S=Object.prototype.hasOwnProperty,K=Object.prototype.propertyIsEnumerable,N=(t,e,o)=>e in t?T(t,e,{enumerable:!0,configurable:!0,writable:!0,value:o}):t[e]=o,I=(t,e)=>{for(var o in e||(e={}))S.call(e,o)&&N(t,o,e[o]);if(P)for(var o of P(e))K.call(e,o)&&N(t,o,e[o]);return t};class W extends((0,t.DataApiMixin)(t.Editor)){constructor(o,i={}){const s=Object.keys(o),r=0===s.length||"string"==typeof o[s[0]];if(r&&void 0!==i.initialData)throw new e.CKEditorError("editor-create-initial-data",null);if(super(i),this._registeredRootsAttributesKeys=new Set,this._readOnlyRootLocks=new Map,this.sourceElements=r?{}:o,void 0===this.config.get("initialData")){const t={};for(const i of s)t[i]=L(n=o[i])?(0,e.getDataFromElement)(n):n;this.config.set("initialData",t)}var n;if(!r)for(const e of s)(0,t.secureSourceElement)(this,o[e]);this.editing.view.document.roots.on("add",((t,e)=>{e.unbind("isReadOnly"),e.bind("isReadOnly").to(this.editing.view.document,"isReadOnly",(t=>t||this._readOnlyRootLocks.has(e.rootName))),e.on("change:isReadOnly",((t,o,i)=>{const s=this.editing.view.createRangeIn(e);for(const t of s.getItems())t.is("editableElement")&&(t.unbind("isReadOnly"),t.isReadOnly=i)}))}));for(const t of s)this.model.document.createRoot("$root",t);if(this.config.get("rootsAttributes")){const t=this.config.get("rootsAttributes");for(const[o,i]of Object.entries(t)){if(!s.includes(o))throw new e.CKEditorError("multi-root-editor-root-attributes-no-root",null);for(const t of Object.keys(i))this._registeredRootsAttributesKeys.add(t)}this.data.on("init",(()=>{this.model.enqueueChange({isUndoable:!1},(e=>{for(const[o,i]of Object.entries(t)){const t=this.model.document.getRoot(o);for(const[o,s]of Object.entries(i))null!==s&&e.setAttribute(o,s,t)}}))}))}const d={shouldToolbarGroupWhenFull:!this.config.get("toolbar.shouldNotGroupWhenFull"),editableElements:r?void 0:o},l=new c(this.locale,this.editing.view,s,d);this.ui=new a(this,l),this.model.document.on("change:data",(()=>{const t=this.model.document.differ.getChangedRoots();for(const e of t){const t=this.model.document.getRoot(e.name);"detached"==e.state&&this.fire("detachRoot",t)}for(const e of t){const t=this.model.document.getRoot(e.name);"attached"==e.state&&this.fire("addRoot",t)}})),this.listenTo(this.model,"canEditAt",((t,[e])=>{if(!e)return;let o=!1;for(const t of e.getRanges()){const e=t.root.rootName;if(this._readOnlyRootLocks.has(e)){o=!0;break}}o&&(t.return=!1,t.stop())}),{priority:"high"})}destroy(){const t=this.config.get("updateSourceElementOnDestroy"),o={};for(const e of Object.keys(this.sourceElements))o[e]=t?this.getData({rootName:e}):"";return this.ui.destroy(),super.destroy().then((()=>{for(const t of Object.keys(this.sourceElements))(0,e.setDataInElement)(this.sourceElements[t],o[t])}))}addRoot(t,{data:e="",attributes:o={},elementName:i="$root",isUndoable:s=!1}={}){const r=this.data,n=this._registeredRootsAttributesKeys;function a(s){const a=s.addRoot(t,i);e&&s.insert(r.parse(e,a),a,0);for(const t of Object.keys(o))n.add(t),s.setAttribute(t,o[t],a)}s?this.model.change(a):this.model.enqueueChange({isUndoable:!1},a)}detachRoot(t,e=!1){e?this.model.change((e=>e.detachRoot(t))):this.model.enqueueChange({isUndoable:!1},(e=>e.detachRoot(t)))}createEditable(t,e){const o=this.ui.view.createEditable(t.rootName);return this.ui.addEditable(o,e),this.editing.view.forceRender(),o.element}detachEditable(t){const e=t.rootName,o=this.ui.view.editables[e];return this.ui.removeEditable(o),this.ui.view.removeEditable(e),o.element}getFullData(t){const e={};for(const i of this.model.document.getRootNames())e[i]=this.data.get((o=I({},t),A(o,F({rootName:i}))));var o;return e}getRootsAttributes(){const t={},e=Array.from(this._registeredRootsAttributesKeys);for(const o of this.model.document.getRootNames()){t[o]={};const i=this.model.document.getRoot(o);for(const s of e)t[o][s]=i.hasAttribute(s)?i.getAttribute(s):null}return t}disableRoot(t,o){if("$graveyard"==t)throw new e.CKEditorError("multi-root-editor-cannot-disable-graveyard-root",this);const i=this._readOnlyRootLocks.get(t);if(i)i.add(o);else{this._readOnlyRootLocks.set(t,new Set([o]));this.editing.view.document.getRoot(t).isReadOnly=!0,Array.from(this.commands.commands()).forEach((t=>t.affectsData&&t.refresh()))}}enableRoot(t,e){const o=this._readOnlyRootLocks.get(t);if(o&&o.has(e))if(1===o.size){this._readOnlyRootLocks.delete(t);this.editing.view.document.getRoot(t).isReadOnly=this.isReadOnly,Array.from(this.commands.commands()).forEach((t=>t.affectsData&&t.refresh()))}else o.delete(e)}static create(t,o={}){return new Promise((i=>{for(const o of Object.values(t))if(L(o)&&"TEXTAREA"===o.tagName)throw new e.CKEditorError("editor-wrong-element",null);const s=new this(t,o);i(s.initPlugins().then((()=>s.ui.init())).then((()=>(s._verifyRootsWithInitialData(),s.data.init(s.config.get("initialData"))))).then((()=>s.fire("ready"))).then((()=>s)))}))}_verifyRootsWithInitialData(){const t=this.config.get("initialData");for(const o of this.model.document.getRootNames())if(!(o in t))throw new e.CKEditorError("multi-root-editor-root-initial-data-mismatch",null);for(const o of Object.keys(t)){const t=this.model.document.getRoot(o);if(!t||!t.isAttached())throw new e.CKEditorError("multi-root-editor-root-initial-data-mismatch",null)}}}function L(t){return D(t)}W.Context=t.Context,W.EditorWatchdog=s.EditorWatchdog,W.ContextWatchdog=s.ContextWatchdog})(),(window.CKEditor5=window.CKEditor5||{}).editorMultiRoot=i})();
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@ckeditor/ckeditor5-editor-multi-root",
|
3
|
-
"version": "
|
3
|
+
"version": "38.0.0-rc.0",
|
4
4
|
"description": "Multi-root editor implementation for CKEditor 5.",
|
5
5
|
"keywords": [
|
6
6
|
"ckeditor",
|
@@ -11,23 +11,25 @@
|
|
11
11
|
],
|
12
12
|
"main": "src/index.js",
|
13
13
|
"dependencies": {
|
14
|
-
"ckeditor5": "^
|
14
|
+
"ckeditor5": "^38.0.0-rc.0",
|
15
15
|
"lodash-es": "^4.17.15"
|
16
16
|
},
|
17
17
|
"devDependencies": {
|
18
|
-
"@ckeditor/ckeditor5-basic-styles": "^
|
19
|
-
"@ckeditor/ckeditor5-core": "^
|
18
|
+
"@ckeditor/ckeditor5-basic-styles": "^38.0.0-rc.0",
|
19
|
+
"@ckeditor/ckeditor5-core": "^38.0.0-rc.0",
|
20
20
|
"@ckeditor/ckeditor5-dev-utils": "^37.0.0",
|
21
|
-
"@ckeditor/ckeditor5-
|
22
|
-
"@ckeditor/ckeditor5-
|
23
|
-
"@ckeditor/ckeditor5-
|
24
|
-
"@ckeditor/ckeditor5-
|
25
|
-
"@ckeditor/ckeditor5-
|
26
|
-
"@ckeditor/ckeditor5-
|
27
|
-
"@ckeditor/ckeditor5-
|
28
|
-
"@ckeditor/ckeditor5-
|
29
|
-
"@ckeditor/ckeditor5-
|
30
|
-
"@ckeditor/ckeditor5-
|
21
|
+
"@ckeditor/ckeditor5-essentials": "^38.0.0-rc.0",
|
22
|
+
"@ckeditor/ckeditor5-engine": "^38.0.0-rc.0",
|
23
|
+
"@ckeditor/ckeditor5-enter": "^38.0.0-rc.0",
|
24
|
+
"@ckeditor/ckeditor5-heading": "^38.0.0-rc.0",
|
25
|
+
"@ckeditor/ckeditor5-paragraph": "^38.0.0-rc.0",
|
26
|
+
"@ckeditor/ckeditor5-theme-lark": "^38.0.0-rc.0",
|
27
|
+
"@ckeditor/ckeditor5-table": "^38.0.0-rc.0",
|
28
|
+
"@ckeditor/ckeditor5-typing": "^38.0.0-rc.0",
|
29
|
+
"@ckeditor/ckeditor5-ui": "^38.0.0-rc.0",
|
30
|
+
"@ckeditor/ckeditor5-undo": "^38.0.0-rc.0",
|
31
|
+
"@ckeditor/ckeditor5-utils": "^38.0.0-rc.0",
|
32
|
+
"@ckeditor/ckeditor5-watchdog": "^38.0.0-rc.0",
|
31
33
|
"typescript": "^4.8.4",
|
32
34
|
"webpack": "^5.58.1",
|
33
35
|
"webpack-cli": "^4.9.0"
|
package/src/multirooteditor.d.ts
CHANGED
@@ -54,6 +54,10 @@ export default class MultiRootEditor extends MultiRootEditor_base {
|
|
54
54
|
* config property and should be returned by {@link #getRootsAttributes}.
|
55
55
|
*/
|
56
56
|
private readonly _registeredRootsAttributesKeys;
|
57
|
+
/**
|
58
|
+
* A set of lock IDs for enabling or disabling particular root.
|
59
|
+
*/
|
60
|
+
private readonly _readOnlyRootLocks;
|
57
61
|
/**
|
58
62
|
* Creates an instance of the multi-root editor.
|
59
63
|
*
|
@@ -255,6 +259,51 @@ export default class MultiRootEditor extends MultiRootEditor_base {
|
|
255
259
|
* @returns Object with roots attributes. Keys are roots names, while values are attributes set on given root.
|
256
260
|
*/
|
257
261
|
getRootsAttributes(): Record<string, RootAttributes>;
|
262
|
+
/**
|
263
|
+
* Switches given editor root to the read-only mode.
|
264
|
+
*
|
265
|
+
* In contrary to {@link module:core/editor/editor~Editor#enableReadOnlyMode `enableReadOnlyMode()`}, which switches the whole editor
|
266
|
+
* to the read-only mode, this method turns only a particular root to the read-only mode. This can be useful when you want to prevent
|
267
|
+
* editing only a part of the editor content.
|
268
|
+
*
|
269
|
+
* When you switch a root to the read-only mode, you need provide a unique identifier (`lockId`) that will identify this request. You
|
270
|
+
* will need to provide the same `lockId` when you will want to
|
271
|
+
* {@link module:editor-multi-root/multirooteditor~MultiRootEditor#enableRoot re-enable} the root.
|
272
|
+
*
|
273
|
+
* ```ts
|
274
|
+
* const model = editor.model;
|
275
|
+
* const myRoot = model.document.getRoot( 'myRoot' );
|
276
|
+
*
|
277
|
+
* editor.disableRoot( 'myRoot', 'my-lock' );
|
278
|
+
* model.canEditAt( myRoot ); // `false`
|
279
|
+
*
|
280
|
+
* editor.disableRoot( 'myRoot', 'other-lock' );
|
281
|
+
* editor.disableRoot( 'myRoot', 'other-lock' ); // Multiple locks with the same ID have no effect.
|
282
|
+
* model.canEditAt( myRoot ); // `false`
|
283
|
+
*
|
284
|
+
* editor.enableRoot( 'myRoot', 'my-lock' );
|
285
|
+
* model.canEditAt( myRoot ); // `false`
|
286
|
+
*
|
287
|
+
* editor.enableRoot( 'myRoot', 'other-lock' );
|
288
|
+
* model.canEditAt( myRoot ); // `true`
|
289
|
+
* ```
|
290
|
+
*
|
291
|
+
* See also {@link module:core/editor/editor~Editor#enableReadOnlyMode `Editor#enableReadOnlyMode()`} and
|
292
|
+
* {@link module:editor-multi-root/multirooteditor~MultiRootEditor#enableRoot `MultiRootEditor#enableRoot()`}.
|
293
|
+
*
|
294
|
+
* @param rootName Name of the root to switch to read-only mode.
|
295
|
+
* @param lockId A unique ID for setting the editor to the read-only state.
|
296
|
+
*/
|
297
|
+
disableRoot(rootName: string, lockId: string | symbol): void;
|
298
|
+
/**
|
299
|
+
* Removes given read-only lock from the given root.
|
300
|
+
*
|
301
|
+
* See {@link module:editor-multi-root/multirooteditor~MultiRootEditor#disableRoot `disableRoot()`}.
|
302
|
+
*
|
303
|
+
* @param rootName Name of the root to switch back from the read-only mode.
|
304
|
+
* @param lockId A unique ID for setting the editor to the read-only state.
|
305
|
+
*/
|
306
|
+
enableRoot(rootName: string, lockId: string | symbol): void;
|
258
307
|
/**
|
259
308
|
* Creates a new multi-root editor instance.
|
260
309
|
*
|
package/src/multirooteditor.js
CHANGED
@@ -67,6 +67,10 @@ export default class MultiRootEditor extends DataApiMixin(Editor) {
|
|
67
67
|
* config property and should be returned by {@link #getRootsAttributes}.
|
68
68
|
*/
|
69
69
|
this._registeredRootsAttributesKeys = new Set();
|
70
|
+
/**
|
71
|
+
* A set of lock IDs for enabling or disabling particular root.
|
72
|
+
*/
|
73
|
+
this._readOnlyRootLocks = new Map();
|
70
74
|
if (!sourceIsData) {
|
71
75
|
this.sourceElements = sourceElementsOrData;
|
72
76
|
}
|
@@ -86,6 +90,25 @@ export default class MultiRootEditor extends DataApiMixin(Editor) {
|
|
86
90
|
secureSourceElement(this, sourceElementsOrData[rootName]);
|
87
91
|
}
|
88
92
|
}
|
93
|
+
this.editing.view.document.roots.on('add', (evt, viewRoot) => {
|
94
|
+
// Here we change the standard binding of readOnly flag by adding
|
95
|
+
// additional constraint that multi-root has (enabling / disabling particular root).
|
96
|
+
viewRoot.unbind('isReadOnly');
|
97
|
+
viewRoot.bind('isReadOnly').to(this.editing.view.document, 'isReadOnly', isReadOnly => {
|
98
|
+
return isReadOnly || this._readOnlyRootLocks.has(viewRoot.rootName);
|
99
|
+
});
|
100
|
+
// Hacky solution to nested editables.
|
101
|
+
// Nested editables should be managed each separately and do not base on view document or view root.
|
102
|
+
viewRoot.on('change:isReadOnly', (evt, prop, value) => {
|
103
|
+
const viewRange = this.editing.view.createRangeIn(viewRoot);
|
104
|
+
for (const viewItem of viewRange.getItems()) {
|
105
|
+
if (viewItem.is('editableElement')) {
|
106
|
+
viewItem.unbind('isReadOnly');
|
107
|
+
viewItem.isReadOnly = value;
|
108
|
+
}
|
109
|
+
}
|
110
|
+
});
|
111
|
+
});
|
89
112
|
for (const rootName of rootNames) {
|
90
113
|
// Create root and `UIView` element for each editable container.
|
91
114
|
this.model.document.createRoot('$root', rootName);
|
@@ -145,6 +168,28 @@ export default class MultiRootEditor extends DataApiMixin(Editor) {
|
|
145
168
|
}
|
146
169
|
}
|
147
170
|
});
|
171
|
+
// Overwrite `Model#canEditAt()` decorated method.
|
172
|
+
// Check if the provided selection is inside a read-only root. If so, return `false`.
|
173
|
+
this.listenTo(this.model, 'canEditAt', (evt, [selection]) => {
|
174
|
+
// Skip empty selections.
|
175
|
+
if (!selection) {
|
176
|
+
return;
|
177
|
+
}
|
178
|
+
let selectionInReadOnlyRoot = false;
|
179
|
+
for (const range of selection.getRanges()) {
|
180
|
+
const rootName = range.root.rootName;
|
181
|
+
if (this._readOnlyRootLocks.has(rootName)) {
|
182
|
+
selectionInReadOnlyRoot = true;
|
183
|
+
break;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
// If selection is in read-only root, return `false` and prevent further processing.
|
187
|
+
// Otherwise, allow for other callbacks (or default callback) to evaluate.
|
188
|
+
if (selectionInReadOnlyRoot) {
|
189
|
+
evt.return = false;
|
190
|
+
evt.stop();
|
191
|
+
}
|
192
|
+
}, { priority: 'high' });
|
148
193
|
}
|
149
194
|
/**
|
150
195
|
* Destroys the editor instance, releasing all resources used by it.
|
@@ -405,6 +450,86 @@ export default class MultiRootEditor extends DataApiMixin(Editor) {
|
|
405
450
|
}
|
406
451
|
return rootsAttributes;
|
407
452
|
}
|
453
|
+
/**
|
454
|
+
* Switches given editor root to the read-only mode.
|
455
|
+
*
|
456
|
+
* In contrary to {@link module:core/editor/editor~Editor#enableReadOnlyMode `enableReadOnlyMode()`}, which switches the whole editor
|
457
|
+
* to the read-only mode, this method turns only a particular root to the read-only mode. This can be useful when you want to prevent
|
458
|
+
* editing only a part of the editor content.
|
459
|
+
*
|
460
|
+
* When you switch a root to the read-only mode, you need provide a unique identifier (`lockId`) that will identify this request. You
|
461
|
+
* will need to provide the same `lockId` when you will want to
|
462
|
+
* {@link module:editor-multi-root/multirooteditor~MultiRootEditor#enableRoot re-enable} the root.
|
463
|
+
*
|
464
|
+
* ```ts
|
465
|
+
* const model = editor.model;
|
466
|
+
* const myRoot = model.document.getRoot( 'myRoot' );
|
467
|
+
*
|
468
|
+
* editor.disableRoot( 'myRoot', 'my-lock' );
|
469
|
+
* model.canEditAt( myRoot ); // `false`
|
470
|
+
*
|
471
|
+
* editor.disableRoot( 'myRoot', 'other-lock' );
|
472
|
+
* editor.disableRoot( 'myRoot', 'other-lock' ); // Multiple locks with the same ID have no effect.
|
473
|
+
* model.canEditAt( myRoot ); // `false`
|
474
|
+
*
|
475
|
+
* editor.enableRoot( 'myRoot', 'my-lock' );
|
476
|
+
* model.canEditAt( myRoot ); // `false`
|
477
|
+
*
|
478
|
+
* editor.enableRoot( 'myRoot', 'other-lock' );
|
479
|
+
* model.canEditAt( myRoot ); // `true`
|
480
|
+
* ```
|
481
|
+
*
|
482
|
+
* See also {@link module:core/editor/editor~Editor#enableReadOnlyMode `Editor#enableReadOnlyMode()`} and
|
483
|
+
* {@link module:editor-multi-root/multirooteditor~MultiRootEditor#enableRoot `MultiRootEditor#enableRoot()`}.
|
484
|
+
*
|
485
|
+
* @param rootName Name of the root to switch to read-only mode.
|
486
|
+
* @param lockId A unique ID for setting the editor to the read-only state.
|
487
|
+
*/
|
488
|
+
disableRoot(rootName, lockId) {
|
489
|
+
if (rootName == '$graveyard') {
|
490
|
+
/**
|
491
|
+
* You cannot disable the `$graveyard` root.
|
492
|
+
*
|
493
|
+
* @error multi-root-editor-cannot-disable-graveyard-root
|
494
|
+
*/
|
495
|
+
throw new CKEditorError('multi-root-editor-cannot-disable-graveyard-root', this);
|
496
|
+
}
|
497
|
+
const locksForGivenRoot = this._readOnlyRootLocks.get(rootName);
|
498
|
+
if (locksForGivenRoot) {
|
499
|
+
locksForGivenRoot.add(lockId);
|
500
|
+
}
|
501
|
+
else {
|
502
|
+
this._readOnlyRootLocks.set(rootName, new Set([lockId]));
|
503
|
+
const editableRootElement = this.editing.view.document.getRoot(rootName);
|
504
|
+
editableRootElement.isReadOnly = true;
|
505
|
+
// Since one of the roots has changed read-only state, we need to refresh all commands that affect data.
|
506
|
+
Array.from(this.commands.commands()).forEach(command => command.affectsData && command.refresh());
|
507
|
+
}
|
508
|
+
}
|
509
|
+
/**
|
510
|
+
* Removes given read-only lock from the given root.
|
511
|
+
*
|
512
|
+
* See {@link module:editor-multi-root/multirooteditor~MultiRootEditor#disableRoot `disableRoot()`}.
|
513
|
+
*
|
514
|
+
* @param rootName Name of the root to switch back from the read-only mode.
|
515
|
+
* @param lockId A unique ID for setting the editor to the read-only state.
|
516
|
+
*/
|
517
|
+
enableRoot(rootName, lockId) {
|
518
|
+
const locksForGivenRoot = this._readOnlyRootLocks.get(rootName);
|
519
|
+
if (!locksForGivenRoot || !locksForGivenRoot.has(lockId)) {
|
520
|
+
return;
|
521
|
+
}
|
522
|
+
if (locksForGivenRoot.size === 1) {
|
523
|
+
this._readOnlyRootLocks.delete(rootName);
|
524
|
+
const editableRootElement = this.editing.view.document.getRoot(rootName);
|
525
|
+
editableRootElement.isReadOnly = this.isReadOnly;
|
526
|
+
// Since one of the roots has changed read-only state, we need to refresh all commands that affect data.
|
527
|
+
Array.from(this.commands.commands()).forEach(command => command.affectsData && command.refresh());
|
528
|
+
}
|
529
|
+
else {
|
530
|
+
locksForGivenRoot.delete(lockId);
|
531
|
+
}
|
532
|
+
}
|
408
533
|
/**
|
409
534
|
* Creates a new multi-root editor instance.
|
410
535
|
*
|
@@ -570,14 +695,15 @@ export default class MultiRootEditor extends DataApiMixin(Editor) {
|
|
570
695
|
for (const rootName of this.model.document.getRootNames()) {
|
571
696
|
if (!(rootName in initialData)) {
|
572
697
|
/**
|
573
|
-
* Editor roots do not match
|
698
|
+
* Editor roots do not match the
|
699
|
+
* {@link module:core/editor/editorconfig~EditorConfig#initialData `initialData` configuration}.
|
574
700
|
*
|
575
|
-
* This
|
701
|
+
* This may happen for one of the two reasons:
|
576
702
|
*
|
577
|
-
* * Configuration error. `sourceElementsOrData` parameter in
|
703
|
+
* * Configuration error. The `sourceElementsOrData` parameter in
|
578
704
|
* {@link module:editor-multi-root/multirooteditor~MultiRootEditor.create `MultiRootEditor.create()`} contains different
|
579
705
|
* roots than {@link module:core/editor/editorconfig~EditorConfig#initialData `initialData` configuration}.
|
580
|
-
* * As the editor was initialized, {@link module:core/editor/editorconfig~EditorConfig#initialData `initialData`}
|
706
|
+
* * As the editor was initialized, the {@link module:core/editor/editorconfig~EditorConfig#initialData `initialData`}
|
581
707
|
* configuration value or the state of the editor roots has been changed.
|
582
708
|
*
|
583
709
|
* @error multi-root-editor-root-initial-data-mismatch
|