mirror of
https://github.com/codex-team/editor.js
synced 2024-06-04 15:02:42 +02:00
onChange improvements (#1678)
* onChange improvements * Return modifications observer module * Fix lint * Fix tests
This commit is contained in:
parent
51d94e1a11
commit
4e7b33c2b8
|
@ -1,5 +1,9 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
### 2.22.0
|
||||||
|
|
||||||
|
- `New` - `onChange` callback now receive Block API object of affected block
|
||||||
|
|
||||||
### 2.21.0
|
### 2.21.0
|
||||||
|
|
||||||
- `New` - Blocks now have unique ids [#873](https://github.com/codex-team/editor.js/issues/873)
|
- `New` - Blocks now have unique ids [#873](https://github.com/codex-team/editor.js/issues/873)
|
||||||
|
|
|
@ -193,7 +193,7 @@ var editor = new EditorJS({
|
||||||
/**
|
/**
|
||||||
* onChange callback
|
* onChange callback
|
||||||
*/
|
*/
|
||||||
onChange: () => {console.log('Now I know that Editor\'s content changed!')}
|
onChange: (editorAPI, affectedBlockAPI) => {console.log('Now I know that Editor\'s content changed!')}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -319,8 +319,8 @@
|
||||||
onReady: function(){
|
onReady: function(){
|
||||||
saveButton.click();
|
saveButton.click();
|
||||||
},
|
},
|
||||||
onChange: function() {
|
onChange: function(api, block) {
|
||||||
console.log('something changed');
|
console.log('something changed', block);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -281,8 +281,8 @@
|
||||||
onReady: function(){
|
onReady: function(){
|
||||||
saveButton.click();
|
saveButton.click();
|
||||||
},
|
},
|
||||||
onChange: function() {
|
onChange: function(api, block) {
|
||||||
console.log('something changed');
|
console.log('something changed', block);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import BlockTool from '../tools/block';
|
||||||
import BlockTune from '../tools/tune';
|
import BlockTune from '../tools/tune';
|
||||||
import { BlockTuneData } from '../../../types/block-tunes/block-tune-data';
|
import { BlockTuneData } from '../../../types/block-tunes/block-tune-data';
|
||||||
import ToolsCollection from '../tools/collection';
|
import ToolsCollection from '../tools/collection';
|
||||||
|
import EventsDispatcher from '../utils/events';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface describes Block class constructor argument
|
* Interface describes Block class constructor argument
|
||||||
|
@ -79,6 +80,11 @@ export enum BlockToolAPI {
|
||||||
ON_PASTE = 'onPaste',
|
ON_PASTE = 'onPaste',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Names of events supported by Block class
|
||||||
|
*/
|
||||||
|
type BlockEvents = 'didMutated';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc Abstract Block class that contains Block information, Tool name and Tool class instance
|
* @classdesc Abstract Block class that contains Block information, Tool name and Tool class instance
|
||||||
*
|
*
|
||||||
|
@ -86,7 +92,7 @@ export enum BlockToolAPI {
|
||||||
* @property {HTMLElement} holder - Div element that wraps block content with Tool's content. Has `ce-block` CSS class
|
* @property {HTMLElement} holder - Div element that wraps block content with Tool's content. Has `ce-block` CSS class
|
||||||
* @property {HTMLElement} pluginsContent - HTML content that returns by Tool's render function
|
* @property {HTMLElement} pluginsContent - HTML content that returns by Tool's render function
|
||||||
*/
|
*/
|
||||||
export default class Block {
|
export default class Block extends EventsDispatcher<BlockEvents> {
|
||||||
/**
|
/**
|
||||||
* CSS classes for the Block
|
* CSS classes for the Block
|
||||||
*
|
*
|
||||||
|
@ -207,6 +213,8 @@ export default class Block {
|
||||||
this.updateCurrentInput();
|
this.updateCurrentInput();
|
||||||
|
|
||||||
this.call(BlockToolAPI.UPDATED);
|
this.call(BlockToolAPI.UPDATED);
|
||||||
|
|
||||||
|
this.emit('didMutated', this);
|
||||||
}, this.modificationDebounceTimer);
|
}, this.modificationDebounceTimer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -230,6 +238,8 @@ export default class Block {
|
||||||
readOnly,
|
readOnly,
|
||||||
tunesData,
|
tunesData,
|
||||||
}: BlockConstructorOptions) {
|
}: BlockConstructorOptions) {
|
||||||
|
super();
|
||||||
|
|
||||||
this.name = tool.name;
|
this.name = tool.name;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.settings = tool.settings;
|
this.settings = tool.settings;
|
||||||
|
@ -680,6 +690,8 @@ export default class Block {
|
||||||
* Call Tool instance destroy method
|
* Call Tool instance destroy method
|
||||||
*/
|
*/
|
||||||
public destroy(): void {
|
public destroy(): void {
|
||||||
|
super.destroy();
|
||||||
|
|
||||||
if (_.isFunction(this.toolInstance.destroy)) {
|
if (_.isFunction(this.toolInstance.destroy)) {
|
||||||
this.toolInstance.destroy();
|
this.toolInstance.destroy();
|
||||||
}
|
}
|
||||||
|
@ -777,6 +789,13 @@ export default class Block {
|
||||||
private addInputEvents(): void {
|
private addInputEvents(): void {
|
||||||
this.inputs.forEach(input => {
|
this.inputs.forEach(input => {
|
||||||
input.addEventListener('focus', this.handleFocus);
|
input.addEventListener('focus', this.handleFocus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If input is native input add oninput listener to observe changes
|
||||||
|
*/
|
||||||
|
if ($.isNativeInput(input)) {
|
||||||
|
input.addEventListener('input', this.didMutated);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -786,6 +805,10 @@ export default class Block {
|
||||||
private removeInputEvents(): void {
|
private removeInputEvents(): void {
|
||||||
this.inputs.forEach(input => {
|
this.inputs.forEach(input => {
|
||||||
input.removeEventListener('focus', this.handleFocus);
|
input.removeEventListener('focus', this.handleFocus);
|
||||||
|
|
||||||
|
if ($.isNativeInput(input)) {
|
||||||
|
input.removeEventListener('input', this.didMutated);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ export default class SanitizerAPI extends Module {
|
||||||
/**
|
/**
|
||||||
* Available methods
|
* Available methods
|
||||||
*
|
*
|
||||||
* @returns {Sanitizer}
|
* @returns {SanitizerConfig}
|
||||||
*/
|
*/
|
||||||
public get methods(): ISanitizer {
|
public get methods(): ISanitizer {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import * as _ from '../utils';
|
||||||
import Blocks from '../blocks';
|
import Blocks from '../blocks';
|
||||||
import { BlockToolData, PasteEvent } from '../../../types';
|
import { BlockToolData, PasteEvent } from '../../../types';
|
||||||
import { BlockTuneData } from '../../../types/block-tunes/block-tune-data';
|
import { BlockTuneData } from '../../../types/block-tunes/block-tune-data';
|
||||||
|
import BlockAPI from '../block/api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {BlockManager} BlockManager
|
* @typedef {BlockManager} BlockManager
|
||||||
|
@ -290,6 +291,11 @@ export default class BlockManager extends Module {
|
||||||
|
|
||||||
this._blocks.insert(newIndex, block, replace);
|
this._blocks.insert(newIndex, block, replace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force call of didMutated event on Block insertion
|
||||||
|
*/
|
||||||
|
this.blockDidMutated(block);
|
||||||
|
|
||||||
if (needToFocus) {
|
if (needToFocus) {
|
||||||
this.currentBlockIndex = newIndex;
|
this.currentBlockIndex = newIndex;
|
||||||
} else if (newIndex <= this.currentBlockIndex) {
|
} else if (newIndex <= this.currentBlockIndex) {
|
||||||
|
@ -361,6 +367,11 @@ export default class BlockManager extends Module {
|
||||||
|
|
||||||
this._blocks[index] = block;
|
this._blocks[index] = block;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force call of didMutated event on Block insertion
|
||||||
|
*/
|
||||||
|
this.blockDidMutated(block);
|
||||||
|
|
||||||
if (needToFocus) {
|
if (needToFocus) {
|
||||||
this.currentBlockIndex = index;
|
this.currentBlockIndex = index;
|
||||||
} else if (index <= this.currentBlockIndex) {
|
} else if (index <= this.currentBlockIndex) {
|
||||||
|
@ -426,8 +437,15 @@ export default class BlockManager extends Module {
|
||||||
throw new Error('Can\'t find a Block to remove');
|
throw new Error('Can\'t find a Block to remove');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const blockToRemove = this._blocks[index];
|
||||||
|
|
||||||
this._blocks.remove(index);
|
this._blocks.remove(index);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force call of didMutated event on Block removal
|
||||||
|
*/
|
||||||
|
this.blockDidMutated(blockToRemove);
|
||||||
|
|
||||||
if (this.currentBlockIndex >= index) {
|
if (this.currentBlockIndex >= index) {
|
||||||
this.currentBlockIndex--;
|
this.currentBlockIndex--;
|
||||||
}
|
}
|
||||||
|
@ -689,6 +707,11 @@ export default class BlockManager extends Module {
|
||||||
|
|
||||||
/** Now actual block moved so that current block index changed */
|
/** Now actual block moved so that current block index changed */
|
||||||
this.currentBlockIndex = toIndex;
|
this.currentBlockIndex = toIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force call of didMutated event on Block movement
|
||||||
|
*/
|
||||||
|
this.blockDidMutated(this.currentBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -754,6 +777,8 @@ export default class BlockManager extends Module {
|
||||||
this.readOnlyMutableListeners.on(block.holder, 'dragleave', (event: DragEvent) => {
|
this.readOnlyMutableListeners.on(block.holder, 'dragleave', (event: DragEvent) => {
|
||||||
BlockEvents.dragLeave(event);
|
BlockEvents.dragLeave(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
block.on('didMutated', (affectedBlock: Block) => this.blockDidMutated(affectedBlock));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -789,4 +814,15 @@ export default class BlockManager extends Module {
|
||||||
private validateIndex(index: number): boolean {
|
private validateIndex(index: number): boolean {
|
||||||
return !(index < 0 || index >= this._blocks.length);
|
return !(index < 0 || index >= this._blocks.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block mutation callback
|
||||||
|
*
|
||||||
|
* @param block - mutated block
|
||||||
|
*/
|
||||||
|
private blockDidMutated(block: Block): Block {
|
||||||
|
this.Editor.ModificationsObserver.onChange(new BlockAPI(block));
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,195 +1,40 @@
|
||||||
/**
|
|
||||||
* @module ModificationsObserver
|
|
||||||
*
|
|
||||||
* Handles any mutations
|
|
||||||
* and gives opportunity to handle outside
|
|
||||||
*/
|
|
||||||
|
|
||||||
import Module from '../__module';
|
import Module from '../__module';
|
||||||
|
import { BlockAPI } from '../../../types';
|
||||||
import * as _ from '../utils';
|
import * as _ from '../utils';
|
||||||
import Block from '../block';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Single entry point for Block mutation events
|
||||||
*/
|
*/
|
||||||
export default class ModificationsObserver extends Module {
|
export default class ModificationsObserver extends Module {
|
||||||
/**
|
/**
|
||||||
* Debounce Timer
|
* Flag shows onChange event is disabled
|
||||||
*
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
public static readonly DebounceTimer = 450;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MutationObserver instance
|
|
||||||
*/
|
|
||||||
private observer: MutationObserver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows to temporary disable mutations handling
|
|
||||||
*/
|
*/
|
||||||
private disabled = false;
|
private disabled = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to prevent several mutation callback execution
|
* Enables onChange event
|
||||||
*
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
private mutationDebouncer = _.debounce(() => {
|
|
||||||
this.updateNativeInputs();
|
|
||||||
|
|
||||||
if (_.isFunction(this.config.onChange)) {
|
|
||||||
this.config.onChange(this.Editor.API.methods);
|
|
||||||
}
|
|
||||||
}, ModificationsObserver.DebounceTimer);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Array of native inputs in Blocks.
|
|
||||||
* Changes in native inputs are not handled by modification observer, so we need to set change event listeners on them
|
|
||||||
*/
|
|
||||||
private nativeInputs: HTMLElement[] = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear timeout and set null to mutationDebouncer property
|
|
||||||
*/
|
|
||||||
public destroy(): void {
|
|
||||||
this.mutationDebouncer = null;
|
|
||||||
if (this.observer) {
|
|
||||||
this.observer.disconnect();
|
|
||||||
}
|
|
||||||
this.observer = null;
|
|
||||||
this.nativeInputs.forEach((input) => this.listeners.off(input, 'input', this.mutationDebouncer));
|
|
||||||
this.mutationDebouncer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set read-only state
|
|
||||||
*
|
|
||||||
* @param {boolean} readOnlyEnabled - read only flag value
|
|
||||||
*/
|
|
||||||
public toggleReadOnly(readOnlyEnabled: boolean): void {
|
|
||||||
if (readOnlyEnabled) {
|
|
||||||
this.disableModule();
|
|
||||||
} else {
|
|
||||||
this.enableModule();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows to disable observer,
|
|
||||||
* for example when Editor wants to stealthy mutate DOM
|
|
||||||
*/
|
|
||||||
public disable(): void {
|
|
||||||
this.disabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables mutation handling
|
|
||||||
* Should be called after .disable()
|
|
||||||
*/
|
*/
|
||||||
public enable(): void {
|
public enable(): void {
|
||||||
this.disabled = false;
|
this.disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setObserver
|
* Disables onChange event
|
||||||
*
|
|
||||||
* sets 'DOMSubtreeModified' listener on Editor's UI.nodes.redactor
|
|
||||||
* so that User can handle outside from API
|
|
||||||
*/
|
*/
|
||||||
private setObserver(): void {
|
public disable(): void {
|
||||||
const { UI } = this.Editor;
|
this.disabled = true;
|
||||||
const observerOptions = {
|
|
||||||
childList: true,
|
|
||||||
attributes: true,
|
|
||||||
subtree: true,
|
|
||||||
characterData: true,
|
|
||||||
characterDataOldValue: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.observer = new MutationObserver((mutationList, observer) => {
|
|
||||||
this.mutationHandler(mutationList, observer);
|
|
||||||
});
|
|
||||||
this.observer.observe(UI.nodes.redactor, observerOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MutationObserver events handler
|
* Call onChange event passed to Editor.js configuration
|
||||||
*
|
*
|
||||||
* @param {MutationRecord[]} mutationList - list of mutations
|
* @param block - changed Block
|
||||||
* @param {MutationObserver} observer - observer instance
|
|
||||||
*/
|
*/
|
||||||
private mutationHandler(mutationList: MutationRecord[], observer: MutationObserver): void {
|
public onChange(block: BlockAPI): void {
|
||||||
/**
|
if (this.disabled || !_.isFunction(this.config.onChange)) {
|
||||||
* Skip mutations in stealth mode
|
|
||||||
*/
|
|
||||||
if (this.disabled) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
this.config.onChange(this.Editor.API.methods, block);
|
||||||
* We divide two Mutation types:
|
|
||||||
* 1) mutations that concerns client changes: settings changes, symbol added, deletion, insertions and so on
|
|
||||||
* 2) functional changes. On each client actions we set functional identifiers to interact with user
|
|
||||||
*/
|
|
||||||
let contentMutated = false;
|
|
||||||
|
|
||||||
mutationList.forEach((mutation) => {
|
|
||||||
switch (mutation.type) {
|
|
||||||
case 'childList':
|
|
||||||
case 'characterData':
|
|
||||||
contentMutated = true;
|
|
||||||
break;
|
|
||||||
case 'attributes':
|
|
||||||
/**
|
|
||||||
* Changes on Element.ce-block usually is functional
|
|
||||||
*/
|
|
||||||
if (!(mutation.target as Element).classList.contains(Block.CSS.wrapper)) {
|
|
||||||
contentMutated = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/** call once */
|
|
||||||
if (contentMutated) {
|
|
||||||
this.mutationDebouncer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets native inputs and set oninput event handler
|
|
||||||
*/
|
|
||||||
private updateNativeInputs(): void {
|
|
||||||
if (this.nativeInputs) {
|
|
||||||
this.nativeInputs.forEach((input) => {
|
|
||||||
this.listeners.off(input, 'input');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nativeInputs = Array.from(this.Editor.UI.nodes.redactor.querySelectorAll('textarea, input, select'));
|
|
||||||
|
|
||||||
this.nativeInputs.forEach((input) => this.listeners.on(input, 'input', this.mutationDebouncer));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets observer and enables it
|
|
||||||
*/
|
|
||||||
private enableModule(): void {
|
|
||||||
/**
|
|
||||||
* wait till Browser render Editor's Blocks
|
|
||||||
*/
|
|
||||||
window.setTimeout(() => {
|
|
||||||
this.setObserver();
|
|
||||||
this.updateNativeInputs();
|
|
||||||
this.enable();
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disables observer
|
|
||||||
*/
|
|
||||||
private disableModule(): void {
|
|
||||||
this.disable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -639,7 +639,7 @@ export default class Paste extends Module {
|
||||||
* @param {PasteData} dataToInsert - data of Block to insert
|
* @param {PasteData} dataToInsert - data of Block to insert
|
||||||
*/
|
*/
|
||||||
private async processInlinePaste(dataToInsert: PasteData): Promise<void> {
|
private async processInlinePaste(dataToInsert: PasteData): Promise<void> {
|
||||||
const { BlockManager, Caret, Tools } = this.Editor;
|
const { BlockManager, Caret } = this.Editor;
|
||||||
const { content } = dataToInsert;
|
const { content } = dataToInsert;
|
||||||
|
|
||||||
const currentBlockIsDefault = BlockManager.currentBlock && BlockManager.currentBlock.tool.isDefault;
|
const currentBlockIsDefault = BlockManager.currentBlock && BlockManager.currentBlock.tool.isDefault;
|
||||||
|
|
|
@ -48,8 +48,15 @@ export default class Renderer extends Module {
|
||||||
public async render(blocks: OutputBlockData[]): Promise<void> {
|
public async render(blocks: OutputBlockData[]): Promise<void> {
|
||||||
const chainData = blocks.map((block) => ({ function: (): Promise<void> => this.insertBlock(block) }));
|
const chainData = blocks.map((block) => ({ function: (): Promise<void> => this.insertBlock(block) }));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable onChange callback on render to not to spam those events
|
||||||
|
*/
|
||||||
|
this.Editor.ModificationsObserver.disable();
|
||||||
|
|
||||||
const sequence = await _.sequence(chainData as _.ChainData[]);
|
const sequence = await _.sequence(chainData as _.ChainData[]);
|
||||||
|
|
||||||
|
this.Editor.ModificationsObserver.enable();
|
||||||
|
|
||||||
this.Editor.UI.checkEmptiness();
|
this.Editor.UI.checkEmptiness();
|
||||||
|
|
||||||
return sequence;
|
return sequence;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
import Module from '../__module';
|
import Module from '../__module';
|
||||||
import { OutputData } from '../../../types';
|
import { OutputData } from '../../../types';
|
||||||
import { ValidatedData } from '../../../types/data-formats';
|
import { SavedData, ValidatedData } from '../../../types/data-formats';
|
||||||
import Block from '../block';
|
import Block from '../block';
|
||||||
import * as _ from '../utils';
|
import * as _ from '../utils';
|
||||||
import { sanitizeBlocks } from '../utils/sanitizer';
|
import { sanitizeBlocks } from '../utils/sanitizer';
|
||||||
|
@ -28,26 +28,28 @@ export default class Saver extends Module {
|
||||||
* @returns {OutputData}
|
* @returns {OutputData}
|
||||||
*/
|
*/
|
||||||
public async save(): Promise<OutputData> {
|
public async save(): Promise<OutputData> {
|
||||||
const { BlockManager, ModificationsObserver, Tools } = this.Editor;
|
const { BlockManager, Tools, ModificationsObserver } = this.Editor;
|
||||||
const blocks = BlockManager.blocks,
|
const blocks = BlockManager.blocks,
|
||||||
chainData = [];
|
chainData = [];
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable modifications observe while saving
|
|
||||||
*/
|
|
||||||
ModificationsObserver.disable();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
/**
|
||||||
|
* Disable onChange callback on save to not to spam those events
|
||||||
|
*/
|
||||||
|
ModificationsObserver.disable();
|
||||||
|
|
||||||
blocks.forEach((block: Block) => {
|
blocks.forEach((block: Block) => {
|
||||||
chainData.push(this.getSavedData(block));
|
chainData.push(this.getSavedData(block));
|
||||||
});
|
});
|
||||||
|
|
||||||
const extractedData = await Promise.all(chainData);
|
const extractedData = await Promise.all(chainData) as Array<Pick<SavedData, 'data' | 'tool'>>;
|
||||||
const sanitizedData = await sanitizeBlocks(extractedData, (name) => {
|
const sanitizedData = await sanitizeBlocks(extractedData, (name) => {
|
||||||
return Tools.blockTools.get(name).sanitizeConfig;
|
return Tools.blockTools.get(name).sanitizeConfig;
|
||||||
});
|
});
|
||||||
|
|
||||||
return this.makeOutput(sanitizedData);
|
return this.makeOutput(sanitizedData);
|
||||||
|
} catch (e) {
|
||||||
|
_.logLabeled(`Saving failed due to the Error %o`, 'error', e);
|
||||||
} finally {
|
} finally {
|
||||||
ModificationsObserver.enable();
|
ModificationsObserver.enable();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,10 +9,8 @@ import { I18nInternalNS } from '../../i18n/namespace-internal';
|
||||||
import Shortcuts from '../../utils/shortcuts';
|
import Shortcuts from '../../utils/shortcuts';
|
||||||
import Tooltip from '../../utils/tooltip';
|
import Tooltip from '../../utils/tooltip';
|
||||||
import { ModuleConfig } from '../../../types-internal/module-config';
|
import { ModuleConfig } from '../../../types-internal/module-config';
|
||||||
import EventsDispatcher from '../../utils/events';
|
|
||||||
import InlineTool from '../../tools/inline';
|
import InlineTool from '../../tools/inline';
|
||||||
import { CommonInternalSettings } from '../../tools/base';
|
import { CommonInternalSettings } from '../../tools/base';
|
||||||
import BlockTool from '../../tools/block';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inline Toolbar elements
|
* Inline Toolbar elements
|
||||||
|
|
|
@ -660,7 +660,7 @@ export function deprecationAssert(condition: boolean, oldProperty: string, newPr
|
||||||
* @param propertyKey - method or accessor name
|
* @param propertyKey - method or accessor name
|
||||||
* @param descriptor - property descriptor
|
* @param descriptor - property descriptor
|
||||||
*/
|
*/
|
||||||
export function cacheable<Target, Value, Arguments extends any[] = any[]>(
|
export function cacheable<Target, Value, Arguments extends unknown[] = unknown[]>(
|
||||||
target: Target,
|
target: Target,
|
||||||
propertyKey: string,
|
propertyKey: string,
|
||||||
descriptor: PropertyDescriptor
|
descriptor: PropertyDescriptor
|
||||||
|
@ -672,7 +672,7 @@ export function cacheable<Target, Value, Arguments extends any[] = any[]>(
|
||||||
/**
|
/**
|
||||||
* Override get or value descriptor property to cache return value
|
* Override get or value descriptor property to cache return value
|
||||||
*
|
*
|
||||||
* @param args
|
* @param args - method args
|
||||||
*/
|
*/
|
||||||
descriptor[propertyToOverride] = function (...args: Arguments): Value {
|
descriptor[propertyToOverride] = function (...args: Arguments): Value {
|
||||||
/**
|
/**
|
||||||
|
@ -688,12 +688,12 @@ export function cacheable<Target, Value, Arguments extends any[] = any[]>(
|
||||||
/**
|
/**
|
||||||
* If get accessor has been overridden, we need to override set accessor to clear cache
|
* If get accessor has been overridden, we need to override set accessor to clear cache
|
||||||
*
|
*
|
||||||
* @param value
|
* @param value - value to set
|
||||||
*/
|
*/
|
||||||
if (propertyToOverride === 'get' && descriptor.set) {
|
if (propertyToOverride === 'get' && descriptor.set) {
|
||||||
const originalSet = descriptor.set;
|
const originalSet = descriptor.set;
|
||||||
|
|
||||||
descriptor.set = function (value: any): void {
|
descriptor.set = function (value: unknown): void {
|
||||||
delete target[cacheKey];
|
delete target[cacheKey];
|
||||||
|
|
||||||
originalSet.apply(this, value);
|
originalSet.apply(this, value);
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
* @typedef {Events} Events
|
* @typedef {Events} Events
|
||||||
* @property {object} subscribers - all subscribers grouped by event name
|
* @property {object} subscribers - all subscribers grouped by event name
|
||||||
*/
|
*/
|
||||||
export default class EventsDispatcher {
|
export default class EventsDispatcher<Events extends string = string> {
|
||||||
/**
|
/**
|
||||||
* Object with events` names as key and array of callback functions as value
|
* Object with events` names as key and array of callback functions as value
|
||||||
*
|
*
|
||||||
|
@ -25,7 +25,7 @@ export default class EventsDispatcher {
|
||||||
* @param {string} eventName - event name
|
* @param {string} eventName - event name
|
||||||
* @param {Function} callback - subscriber
|
* @param {Function} callback - subscriber
|
||||||
*/
|
*/
|
||||||
public on(eventName: string, callback: (data: object) => object): void {
|
public on(eventName: Events, callback: (data: object) => object): void {
|
||||||
if (!(eventName in this.subscribers)) {
|
if (!(eventName in this.subscribers)) {
|
||||||
this.subscribers[eventName] = [];
|
this.subscribers[eventName] = [];
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ export default class EventsDispatcher {
|
||||||
* @param {string} eventName - event name
|
* @param {string} eventName - event name
|
||||||
* @param {Function} callback - subscriber
|
* @param {Function} callback - subscriber
|
||||||
*/
|
*/
|
||||||
public once(eventName: string, callback: (data: object) => object): void {
|
public once(eventName: Events, callback: (data: object) => object): void {
|
||||||
if (!(eventName in this.subscribers)) {
|
if (!(eventName in this.subscribers)) {
|
||||||
this.subscribers[eventName] = [];
|
this.subscribers[eventName] = [];
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ export default class EventsDispatcher {
|
||||||
* @param {string} eventName - event name
|
* @param {string} eventName - event name
|
||||||
* @param {object} data - subscribers get this data when they were fired
|
* @param {object} data - subscribers get this data when they were fired
|
||||||
*/
|
*/
|
||||||
public emit(eventName: string, data?: object): void {
|
public emit(eventName: Events, data?: object): void {
|
||||||
if (!this.subscribers[eventName]) {
|
if (!this.subscribers[eventName]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +85,7 @@ export default class EventsDispatcher {
|
||||||
* @param {string} eventName - event name
|
* @param {string} eventName - event name
|
||||||
* @param {Function} callback - event handler
|
* @param {Function} callback - event handler
|
||||||
*/
|
*/
|
||||||
public off(eventName: string, callback: (data: object) => object): void {
|
public off(eventName: Events, callback: (data: object) => object): void {
|
||||||
for (let i = 0; i < this.subscribers[eventName].length; i++) {
|
for (let i = 0; i < this.subscribers[eventName].length; i++) {
|
||||||
if (this.subscribers[eventName][i] === callback) {
|
if (this.subscribers[eventName][i] === callback) {
|
||||||
delete this.subscribers[eventName][i];
|
delete this.subscribers[eventName][i];
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-use-before-define */
|
||||||
/**
|
/**
|
||||||
* CodeX Sanitizer
|
* CodeX Sanitizer
|
||||||
*
|
*
|
||||||
|
|
4
src/types-internal/editor-modules.d.ts
vendored
4
src/types-internal/editor-modules.d.ts
vendored
|
@ -6,7 +6,6 @@ import Toolbox from '../components/modules/toolbar/toolbox';
|
||||||
import BlockSettings from '../components/modules/toolbar/blockSettings';
|
import BlockSettings from '../components/modules/toolbar/blockSettings';
|
||||||
import Paste from '../components/modules/paste';
|
import Paste from '../components/modules/paste';
|
||||||
import DragNDrop from '../components/modules/dragNDrop';
|
import DragNDrop from '../components/modules/dragNDrop';
|
||||||
import ModificationsObserver from '../components/modules/modificationsObserver';
|
|
||||||
import Renderer from '../components/modules/renderer';
|
import Renderer from '../components/modules/renderer';
|
||||||
import Tools from '../components/modules/tools';
|
import Tools from '../components/modules/tools';
|
||||||
import API from '../components/modules/api/index';
|
import API from '../components/modules/api/index';
|
||||||
|
@ -32,6 +31,7 @@ import TooltipAPI from '../components/modules/api/tooltip';
|
||||||
import ReadOnly from '../components/modules/readonly';
|
import ReadOnly from '../components/modules/readonly';
|
||||||
import ReadOnlyAPI from '../components/modules/api/readonly';
|
import ReadOnlyAPI from '../components/modules/api/readonly';
|
||||||
import I18nAPI from '../components/modules/api/i18n';
|
import I18nAPI from '../components/modules/api/i18n';
|
||||||
|
import ModificationsObserver from '../components/modules/modificationsObserver';
|
||||||
|
|
||||||
export interface EditorModules {
|
export interface EditorModules {
|
||||||
UI: UI;
|
UI: UI;
|
||||||
|
@ -45,7 +45,6 @@ export interface EditorModules {
|
||||||
ConversionToolbar: ConversionToolbar;
|
ConversionToolbar: ConversionToolbar;
|
||||||
Paste: Paste;
|
Paste: Paste;
|
||||||
DragNDrop: DragNDrop;
|
DragNDrop: DragNDrop;
|
||||||
ModificationsObserver: ModificationsObserver;
|
|
||||||
Renderer: Renderer;
|
Renderer: Renderer;
|
||||||
Tools: Tools;
|
Tools: Tools;
|
||||||
API: API;
|
API: API;
|
||||||
|
@ -68,4 +67,5 @@ export interface EditorModules {
|
||||||
ReadOnly: ReadOnly;
|
ReadOnly: ReadOnly;
|
||||||
ReadOnlyAPI: ReadOnlyAPI;
|
ReadOnlyAPI: ReadOnlyAPI;
|
||||||
I18nAPI: I18nAPI;
|
I18nAPI: I18nAPI;
|
||||||
|
ModificationsObserver: ModificationsObserver;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
/* tslint:disable:no-var-requires */
|
/* tslint:disable:no-var-requires */
|
||||||
/**
|
/**
|
||||||
* This file contains connection of Cypres plugins
|
* This file contains connection of Cypres plugins
|
||||||
|
@ -6,7 +7,7 @@ const webpackConfig = require('../../../webpack.config.js');
|
||||||
const preprocessor = require('@cypress/webpack-preprocessor');
|
const preprocessor = require('@cypress/webpack-preprocessor');
|
||||||
const codeCoverageTask = require('@cypress/code-coverage/task');
|
const codeCoverageTask = require('@cypress/code-coverage/task');
|
||||||
|
|
||||||
module.exports = (on, config): any => {
|
module.exports = (on, config): unknown => {
|
||||||
/**
|
/**
|
||||||
* Add Cypress task to get code coverage
|
* Add Cypress task to get code coverage
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/**
|
/**
|
||||||
* This file contains custom commands for Cypress.
|
* This file contains custom commands for Cypress.
|
||||||
* Also it can override the existing commands.
|
* Also it can override the existing commands.
|
||||||
|
@ -40,7 +41,7 @@ Cypress.Commands.add('createEditor', (editorConfig: EditorConfig = {}): Chainabl
|
||||||
/**
|
/**
|
||||||
* Paste command to dispatch paste event
|
* Paste command to dispatch paste event
|
||||||
*
|
*
|
||||||
* @usage
|
* Usage
|
||||||
* cy.get('div').paste({'text/plain': 'Text', 'text/html': '<b>Text</b>'})
|
* cy.get('div').paste({'text/plain': 'Text', 'text/html': '<b>Text</b>'})
|
||||||
*
|
*
|
||||||
* @param data - map with MIME type as a key and data as value
|
* @param data - map with MIME type as a key and data as value
|
||||||
|
@ -66,7 +67,7 @@ Cypress.Commands.add('paste', {
|
||||||
/**
|
/**
|
||||||
* Copy command to dispatch copy event on subject
|
* Copy command to dispatch copy event on subject
|
||||||
*
|
*
|
||||||
* @usage
|
* Usage:
|
||||||
* cy.get('div').copy().then(data => {})
|
* cy.get('div').copy().then(data => {})
|
||||||
*/
|
*/
|
||||||
Cypress.Commands.add('copy', { prevSubject: true }, async (subject) => {
|
Cypress.Commands.add('copy', { prevSubject: true }, async (subject) => {
|
||||||
|
@ -92,7 +93,7 @@ Cypress.Commands.add('copy', { prevSubject: true }, async (subject) => {
|
||||||
/**
|
/**
|
||||||
* Cut command to dispatch cut event on subject
|
* Cut command to dispatch cut event on subject
|
||||||
*
|
*
|
||||||
* @usage
|
* Usage:
|
||||||
* cy.get('div').cut().then(data => {})
|
* cy.get('div').cut().then(data => {})
|
||||||
*/
|
*/
|
||||||
Cypress.Commands.add('cut', { prevSubject: true }, async (subject) => {
|
Cypress.Commands.add('cut', { prevSubject: true }, async (subject) => {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/**
|
/**
|
||||||
* There will be described test cases of 'blocks.*' API
|
* There will be described test cases of 'blocks.*' API
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import Header from '../../../example/tools/header';
|
import Header from '../../../example/tools/header';
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/* tslint:disable:max-classes-per-file */
|
/* tslint:disable:max-classes-per-file */
|
||||||
/* eslint-disable @typescript-eslint/ban-ts-ignore */
|
/* eslint-disable @typescript-eslint/ban-ts-ignore,@typescript-eslint/no-explicit-any,jsdoc/require-jsdoc */
|
||||||
import Tools from '../../../../src/components/modules/tools';
|
import Tools from '../../../../src/components/modules/tools';
|
||||||
import { EditorConfig } from '../../../../types';
|
import { EditorConfig } from '../../../../types';
|
||||||
import BlockTool from '../../../../src/components/tools/block';
|
import BlockTool from '../../../../src/components/tools/block';
|
||||||
|
@ -86,8 +86,8 @@ describe('Tools module', () => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
public static prepare(): void {}
|
public static prepare(): void {}
|
||||||
} as any,
|
} as any,
|
||||||
inlineToolbar: ['inlineTool2'],
|
inlineToolbar: [ 'inlineTool2' ],
|
||||||
tunes: ['blockTune2']
|
tunes: [ 'blockTune2' ],
|
||||||
},
|
},
|
||||||
withFailedPrepare: class {
|
withFailedPrepare: class {
|
||||||
public static prepare(): void {
|
public static prepare(): void {
|
||||||
|
|
127
test/cypress/tests/onchange.spec.ts
Normal file
127
test/cypress/tests/onchange.spec.ts
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
import Header from '../../../example/tools/header';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @todo Add checks that correct block API object is passed to onChange
|
||||||
|
* @todo Add cases for native inputs changes
|
||||||
|
*/
|
||||||
|
describe('onChange callback', () => {
|
||||||
|
const config = {
|
||||||
|
tools: {
|
||||||
|
header: Header,
|
||||||
|
},
|
||||||
|
onChange: (): void => {
|
||||||
|
console.log('something changed');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
if (this && this.editorInstance) {
|
||||||
|
this.editorInstance.destroy();
|
||||||
|
} else {
|
||||||
|
cy.spy(config, 'onChange').as('onChange');
|
||||||
|
|
||||||
|
cy.createEditor(config).as('editorInstance');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fire onChange callback on block insertion', () => {
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-block')
|
||||||
|
.click()
|
||||||
|
.type('{enter}');
|
||||||
|
|
||||||
|
cy.get('@onChange').should('be.called');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fire onChange callback on typing into block', () => {
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-block')
|
||||||
|
.click()
|
||||||
|
.type('some text');
|
||||||
|
|
||||||
|
cy.get('@onChange').should('be.called');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fire onChange callback on block replacement', () => {
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-block')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-toolbar__plus')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('li.ce-toolbox__button[data-tool=header]')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('@onChange').should('be.calledWithMatch', Cypress.sinon.match.any, Cypress.sinon.match({ name: 'header' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fire onChange callback on tune modifier', () => {
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-block')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-toolbar__plus')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('li.ce-toolbox__button[data-tool=header]')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-block')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('span.ce-toolbar__settings-btn')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('span.cdx-settings-button[data-level=1]')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('@onChange').should('be.calledWithMatch', Cypress.sinon.match.any, Cypress.sinon.match({ name: 'header' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fire onChange callback when block is removed', () => {
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-block')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('span.ce-toolbar__settings-btn')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-settings__button--delete')
|
||||||
|
.click()
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('@onChange').should('be.called');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fire onChange callback when block is moved', () => {
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-block')
|
||||||
|
.click()
|
||||||
|
.type('{enter}');
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-block')
|
||||||
|
.last()
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('span.ce-toolbar__settings-btn')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('[data-cy=editorjs]')
|
||||||
|
.get('div.ce-tune-move-up')
|
||||||
|
.click();
|
||||||
|
|
||||||
|
cy.get('@onChange').should('be.called');
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
describe('Output sanitisation', () => {
|
describe('Output sanitisation', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
if (this && this.editorInstance) {
|
if (this && this.editorInstance) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* tslint:disable:max-classes-per-file */
|
/* tslint:disable:max-classes-per-file */
|
||||||
import { BlockToolData, ToolSettings } from '../../../../types';
|
import { BlockToolData, ToolSettings } from '../../../../types';
|
||||||
import { ToolType } from '../../../../src/components/tools/base';
|
import { ToolType } from '../../../../src/components/tools/base';
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* tslint:disable:max-classes-per-file */
|
/* tslint:disable:max-classes-per-file */
|
||||||
import { ToolSettings } from '../../../../types';
|
import { ToolSettings } from '../../../../types';
|
||||||
import { ToolType } from '../../../../src/components/tools/base';
|
import { ToolType } from '../../../../src/components/tools/base';
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
/* tslint:disable:max-classes-per-file */
|
/* tslint:disable:max-classes-per-file */
|
||||||
import { ToolSettings } from '../../../../types';
|
import { ToolSettings } from '../../../../types';
|
||||||
import { ToolType } from '../../../../src/components/tools/base';
|
import { ToolType } from '../../../../src/components/tools/base';
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import ToolsCollection from '../../../../src/components/tools/collection';
|
import ToolsCollection from '../../../../src/components/tools/collection';
|
||||||
import BlockTool from '../../../../src/components/tools/block';
|
import BlockTool from '../../../../src/components/tools/block';
|
||||||
import InlineTool from '../../../../src/components/tools/inline';
|
import InlineTool from '../../../../src/components/tools/inline';
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import LinkInlineTool from '../../../../src/components/inline-tools/inline-tool-link';
|
import LinkInlineTool from '../../../../src/components/inline-tools/inline-tool-link';
|
||||||
import MoveUpTune from '../../../../src/components/block-tunes/block-tune-move-up';
|
import MoveUpTune from '../../../../src/components/block-tunes/block-tune-move-up';
|
||||||
import ToolsFactory from '../../../../src/components/tools/factory';
|
import ToolsFactory from '../../../../src/components/tools/factory';
|
||||||
|
|
5
types/configs/editor-config.d.ts
vendored
5
types/configs/editor-config.d.ts
vendored
|
@ -1,5 +1,5 @@
|
||||||
import {ToolConstructable, ToolSettings} from '../tools';
|
import {ToolConstructable, ToolSettings} from '../tools';
|
||||||
import {API, LogLevels, OutputData} from '../index';
|
import {API, BlockAPI, LogLevels, OutputData} from '../index';
|
||||||
import {SanitizerConfig} from './sanitizer-config';
|
import {SanitizerConfig} from './sanitizer-config';
|
||||||
import {I18nConfig} from './i18n-config';
|
import {I18nConfig} from './i18n-config';
|
||||||
|
|
||||||
|
@ -88,8 +88,9 @@ export interface EditorConfig {
|
||||||
/**
|
/**
|
||||||
* Fires when something changed in DOM
|
* Fires when something changed in DOM
|
||||||
* @param {API} api - editor.js api
|
* @param {API} api - editor.js api
|
||||||
|
* @param block - changed block API
|
||||||
*/
|
*/
|
||||||
onChange?(api: API): void;
|
onChange?(api: API, block: BlockAPI): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines default toolbar for all tools.
|
* Defines default toolbar for all tools.
|
||||||
|
|
Loading…
Reference in a new issue