editor.js/src/components/modules/readonly.ts
Peter 91959bba43
fix(on-change): onchange callback wont be fired in readonly (#2773)
* fix(on-change): onchange callback wont be fired in readonly

* do not rerender blocks on initial call
2024-07-10 19:16:36 +03:00

128 lines
3.2 KiB
TypeScript

import Module from '../__module';
import { CriticalError } from '../errors/critical';
/**
* @module ReadOnly
*
* Has one important method:
* - {Function} toggleReadonly - Set read-only mode or toggle current state
* @version 1.0.0
* @typedef {ReadOnly} ReadOnly
* @property {boolean} readOnlyEnabled - read-only state
*/
export default class ReadOnly extends Module {
/**
* Array of tools name which don't support read-only mode
*/
private toolsDontSupportReadOnly: string[] = [];
/**
* Value to track read-only state
*
* @type {boolean}
*/
private readOnlyEnabled = false;
/**
* Returns state of read only mode
*/
public get isEnabled(): boolean {
return this.readOnlyEnabled;
}
/**
* Set initial state
*/
public async prepare(): Promise<void> {
const { Tools } = this.Editor;
const { blockTools } = Tools;
const toolsDontSupportReadOnly: string[] = [];
Array
.from(blockTools.entries())
.forEach(([name, tool]) => {
if (!tool.isReadOnlySupported) {
toolsDontSupportReadOnly.push(name);
}
});
this.toolsDontSupportReadOnly = toolsDontSupportReadOnly;
if (this.config.readOnly && toolsDontSupportReadOnly.length > 0) {
this.throwCriticalError();
}
this.toggle(this.config.readOnly, true);
}
/**
* Set read-only mode or toggle current state
* Call all Modules `toggleReadOnly` method and re-render Editor
*
* @param state - (optional) read-only state or toggle
* @param isInitial - (optional) true when editor is initializing
*/
public async toggle(state = !this.readOnlyEnabled, isInitial = false): Promise<boolean> {
if (state && this.toolsDontSupportReadOnly.length > 0) {
this.throwCriticalError();
}
const oldState = this.readOnlyEnabled;
this.readOnlyEnabled = state;
for (const name in this.Editor) {
/**
* Verify module has method `toggleReadOnly` method
*/
if (!this.Editor[name].toggleReadOnly) {
continue;
}
/**
* set or toggle read-only state
*/
this.Editor[name].toggleReadOnly(state);
}
/**
* If new state equals old one, do not re-render blocks
*/
if (oldState === state) {
return this.readOnlyEnabled;
}
/**
* Do not re-render blocks if it's initial call
*/
if (isInitial) {
return this.readOnlyEnabled;
}
/**
* Mutex for modifications observer to prevent onChange call when read-only mode is enabled
*/
this.Editor.ModificationsObserver.disable();
/**
* Save current Editor Blocks and render again
*/
const savedBlocks = await this.Editor.Saver.save();
await this.Editor.BlockManager.clear();
await this.Editor.Renderer.render(savedBlocks.blocks);
this.Editor.ModificationsObserver.enable();
return this.readOnlyEnabled;
}
/**
* Throws an error about tools which don't support read-only mode
*/
private throwCriticalError(): never {
throw new CriticalError(
`To enable read-only mode all connected tools should support it. Tools ${this.toolsDontSupportReadOnly.join(', ')} don't support read-only mode.`
);
}
}