mirror of
https://github.com/codex-team/editor.js
synced 2024-06-04 15:02:42 +02:00
Mutation callback (#444)
* modification observer initials * add debouncer to callback execution * change feature name * update * code improvements * tslint fixes * use debouncer from utils * add types * upgrade * fix * update
This commit is contained in:
parent
2c0d747035
commit
7acf321454
|
@ -1,6 +1,6 @@
|
||||||
<p align="center"><img src="https://capella.pics/3c0b525b-50d9-4720-8aad-9148114cfa6e.jpg"></p>
|
<p align="center"><img src="https://capella.pics/3c0b525b-50d9-4720-8aad-9148114cfa6e.jpg"></p>
|
||||||
|
|
||||||
![](https://flat.badgen.net/badge/CodeX%20Editor/v2.0.9/blue?icon=npm)
|
![](https://flat.badgen.net/badge/CodeX%20Editor/v2.0.10/blue?icon=npm)
|
||||||
|
|
||||||
## Version 2.0-beta is here!
|
## Version 2.0-beta is here!
|
||||||
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -177,6 +177,26 @@ editor.saver.save()
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
Also CodeX Editor provides useful methods to work with Editor's state.
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var editor = new CodexEditor({
|
||||||
|
// Other configuration properties
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onReady callback
|
||||||
|
*/
|
||||||
|
onReady: () => {console.log('CodeX Editor is ready to work!')},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* onChange callback
|
||||||
|
*/
|
||||||
|
onChange: () => {console.log('Now I know that Editor\'s content changed!')}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|
||||||
Take a look at the [example.html](../example/example.html) to view more detailed examples.
|
Take a look at the [example.html](../example/example.html) to view more detailed examples.
|
||||||
|
|
|
@ -226,6 +226,9 @@
|
||||||
},
|
},
|
||||||
onReady: function(){
|
onReady: function(){
|
||||||
saveButton.click();
|
saveButton.click();
|
||||||
|
},
|
||||||
|
onChange: function() {
|
||||||
|
console.log('something changed');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "codex.editor",
|
"name": "codex.editor",
|
||||||
"version": "2.0.9",
|
"version": "2.0.10",
|
||||||
"description": "Codex Editor. Native JS, based on API and Open Source",
|
"description": "Codex Editor. Native JS, based on API and Open Source",
|
||||||
"main": "build/codex-editor.js",
|
"main": "build/codex-editor.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
@ -66,6 +66,7 @@ export default class CodexEditor {
|
||||||
const destroy = () => {
|
const destroy = () => {
|
||||||
editor.moduleInstances.Listeners.removeAll();
|
editor.moduleInstances.Listeners.removeAll();
|
||||||
editor.moduleInstances.UI.destroy();
|
editor.moduleInstances.UI.destroy();
|
||||||
|
editor.moduleInstances.ModificationsObserver.destroy();
|
||||||
editor = null;
|
editor = null;
|
||||||
|
|
||||||
for (const field in this) {
|
for (const field in this) {
|
||||||
|
|
|
@ -90,7 +90,7 @@ export default class Core {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setting for configuration
|
* Setting for configuration
|
||||||
* @param {EditorConfig|string|null} config
|
* @param {IEditorConfig|string|null} config
|
||||||
*/
|
*/
|
||||||
set configuration(config) {
|
set configuration(config) {
|
||||||
/**
|
/**
|
||||||
|
@ -129,6 +129,7 @@ export default class Core {
|
||||||
this.config.tools = config.tools || {};
|
this.config.tools = config.tools || {};
|
||||||
this.config.data = config.data || {};
|
this.config.data = config.data || {};
|
||||||
this.config.onReady = config.onReady || function () {};
|
this.config.onReady = config.onReady || function () {};
|
||||||
|
this.config.onChange = config.onChange || function () {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize Blocks to pass data to the Renderer
|
* Initialize Blocks to pass data to the Renderer
|
||||||
|
@ -261,7 +262,7 @@ export default class Core {
|
||||||
* @return {Promise}
|
* @return {Promise}
|
||||||
*/
|
*/
|
||||||
async start() {
|
async start() {
|
||||||
const modulesToPrepare = ['Tools', 'UI', 'BlockManager', 'Paste'];
|
const modulesToPrepare = ['Tools', 'UI', 'BlockManager', 'Paste', 'ModificationsObserver'];
|
||||||
|
|
||||||
await modulesToPrepare.reduce(
|
await modulesToPrepare.reduce(
|
||||||
(promise, module) => promise.then(async () => {
|
(promise, module) => promise.then(async () => {
|
||||||
|
|
|
@ -49,4 +49,9 @@ export default interface IEditorConfig {
|
||||||
* Editor initialization callback
|
* Editor initialization callback
|
||||||
*/
|
*/
|
||||||
onReady?(): void;
|
onReady?(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger callback if Content has beed changed
|
||||||
|
*/
|
||||||
|
onChange?(): void;
|
||||||
}
|
}
|
||||||
|
|
73
src/components/modules/modificationsObserver.ts
Normal file
73
src/components/modules/modificationsObserver.ts
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/**
|
||||||
|
* @module ModificationsObserver
|
||||||
|
*
|
||||||
|
* Handles any mutations
|
||||||
|
* and gives opportunity to handle outside
|
||||||
|
*/
|
||||||
|
|
||||||
|
import IEditorConfig from '../interfaces/editor-config';
|
||||||
|
|
||||||
|
declare const Module: any;
|
||||||
|
declare const _: any;
|
||||||
|
|
||||||
|
export default class ModificationsObserver extends Module {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debounce Timer
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
public static readonly DebounceTimer = 450;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to prevent several mutation callback execution
|
||||||
|
* @type {Function}
|
||||||
|
*/
|
||||||
|
private mutationDebouncer = _.debounce( () => {
|
||||||
|
this.config.onChange.call();
|
||||||
|
}, ModificationsObserver.DebounceTimer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param {IEditorConfig} config
|
||||||
|
*/
|
||||||
|
constructor({config}) {
|
||||||
|
super({config});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear timeout and set null to mutationDebouncer property
|
||||||
|
*/
|
||||||
|
public destroy() {
|
||||||
|
this.mutationDebouncer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preparation method
|
||||||
|
* @return {Promise<void>}
|
||||||
|
*/
|
||||||
|
public async prepare(): Promise<void> {
|
||||||
|
/**
|
||||||
|
* wait till Browser render Editor's Blocks
|
||||||
|
*/
|
||||||
|
window.setTimeout( () => {
|
||||||
|
this.setObserver();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* setObserver
|
||||||
|
*
|
||||||
|
* sets 'DOMSubtreeModified' listener on Editor's UI.nodes.redactor
|
||||||
|
* so that User can handle outside from API
|
||||||
|
*/
|
||||||
|
private setObserver(): void {
|
||||||
|
const {Listeners, UI} = this.Editor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set Listener to the Editor <div> element that holds only Blocks
|
||||||
|
*/
|
||||||
|
Listeners.on(UI.nodes.redactor, 'DOMSubtreeModified', () => {
|
||||||
|
this.mutationDebouncer();
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -196,4 +196,39 @@ export default class Util {
|
||||||
window.setTimeout(() => method.apply(context, args), timeout);
|
window.setTimeout(() => method.apply(context, args), timeout);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Debouncing method
|
||||||
|
* Call method after passed time
|
||||||
|
*
|
||||||
|
* Note that this method returns Function and declared variable need to be called
|
||||||
|
*
|
||||||
|
* @param {Function} func - function that we're throttling
|
||||||
|
* @param {Number} wait - time in milliseconds
|
||||||
|
* @param {Boolean} immediate - call now
|
||||||
|
* @return {Function}
|
||||||
|
*/
|
||||||
|
public static debounce(func: () => void, wait: number , immediate: boolean): () => void {
|
||||||
|
let timeout;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
const context = this,
|
||||||
|
args = arguments;
|
||||||
|
|
||||||
|
const later = () => {
|
||||||
|
timeout = null;
|
||||||
|
if (!immediate) {
|
||||||
|
func.apply(context, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const callNow = immediate && !timeout;
|
||||||
|
|
||||||
|
window.clearTimeout(timeout);
|
||||||
|
timeout = window.setTimeout(later, wait);
|
||||||
|
if (callNow) {
|
||||||
|
func.apply(context, args);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue