Merge data overrides with actual block data when inserting a block

This commit is contained in:
Tanya Fomina 2022-06-05 13:01:46 +08:00
parent f2fe90b65c
commit eb0a59cc64
10 changed files with 104 additions and 90 deletions

View file

@ -228,15 +228,15 @@ export default class BlocksAPI extends Module {
* @param {boolean?} needToFocus - flag to focus inserted Block * @param {boolean?} needToFocus - flag to focus inserted Block
* @param replace - pass true to replace the Block existed under passed index * @param replace - pass true to replace the Block existed under passed index
*/ */
public insert = ( public insert = async (
type: string = this.config.defaultBlock, type: string = this.config.defaultBlock,
data: BlockToolData = {}, data: BlockToolData = {},
config: ToolConfig = {}, config: ToolConfig = {},
index?: number, index?: number,
needToFocus?: boolean, needToFocus?: boolean,
replace?: boolean replace?: boolean
): BlockAPIInterface => { ): Promise<BlockAPIInterface> => {
const insertedBlock = this.Editor.BlockManager.insert({ const insertedBlock = await this.Editor.BlockManager.insert({
tool: type, tool: type,
data, data,
index, index,
@ -267,7 +267,7 @@ export default class BlocksAPI extends Module {
* @param id - id of the block to update * @param id - id of the block to update
* @param data - the new data * @param data - the new data
*/ */
public update = (id: string, data: BlockToolData): void => { public update = async (id: string, data: BlockToolData): Promise<void> => {
const { BlockManager } = this.Editor; const { BlockManager } = this.Editor;
const block = BlockManager.getBlockById(id); const block = BlockManager.getBlockById(id);
@ -278,8 +278,7 @@ export default class BlocksAPI extends Module {
} }
const blockIndex = BlockManager.getBlockIndex(block); const blockIndex = BlockManager.getBlockIndex(block);
await BlockManager.insert({
BlockManager.insert({
id: block.id, id: block.id,
tool: block.name, tool: block.name,
data, data,

View file

@ -191,13 +191,13 @@ export default class BlockEvents extends Module {
return; return;
} }
BlockSelection.copySelectedBlocks(event).then(() => { BlockSelection.copySelectedBlocks(event).then(async () => {
const selectionPositionIndex = BlockManager.removeSelectedBlocks(); const selectionPositionIndex = BlockManager.removeSelectedBlocks();
/** /**
* Insert default block in place of removed ones * Insert default block in place of removed ones
*/ */
const insertedBlock = BlockManager.insertDefaultBlockAtIndex(selectionPositionIndex, true); const insertedBlock = await BlockManager.insertDefaultBlockAtIndex(selectionPositionIndex, true);
Caret.setToBlock(insertedBlock, Caret.positions.START); Caret.setToBlock(insertedBlock, Caret.positions.START);
@ -211,7 +211,7 @@ export default class BlockEvents extends Module {
* *
* @param {KeyboardEvent} event - keydown * @param {KeyboardEvent} event - keydown
*/ */
private enter(event: KeyboardEvent): void { private async enter(event: KeyboardEvent): Promise<void> {
const { BlockManager, UI } = this.Editor; const { BlockManager, UI } = this.Editor;
const currentBlock = BlockManager.currentBlock; const currentBlock = BlockManager.currentBlock;
@ -250,7 +250,7 @@ export default class BlockEvents extends Module {
* Split the Current Block into two blocks * Split the Current Block into two blocks
* Renew local current node after split * Renew local current node after split
*/ */
newCurrent = this.Editor.BlockManager.split(); newCurrent = await this.Editor.BlockManager.split();
} }
this.Editor.Caret.setToBlock(newCurrent); this.Editor.Caret.setToBlock(newCurrent);

View file

@ -15,6 +15,7 @@ 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'; import BlockAPI from '../block/api';
import { BlockMutationType } from '../../../types/events/block/mutation-type'; import { BlockMutationType } from '../../../types/events/block/mutation-type';
import BlockTool from '../tools/block';
/** /**
* @typedef {BlockManager} BlockManager * @typedef {BlockManager} BlockManager
@ -232,23 +233,23 @@ export default class BlockManager extends Module {
* *
* @returns {Block} * @returns {Block}
*/ */
public composeBlock({ public async composeBlock({
tool: name, tool: name,
data = {}, data = {},
id = undefined, id = undefined,
tunes: tunesData = {}, tunes: tunesData = {},
}: {tool: string; id?: string; data?: BlockToolData; tunes?: {[name: string]: BlockTuneData}}): Block { }: { tool: string; id?: string; data?: BlockToolData; tunes?: { [name: string]: BlockTuneData } }): Promise<Block> {
const readOnly = this.Editor.ReadOnly.isEnabled; const readOnly = this.Editor.ReadOnly.isEnabled;
const tool = this.Editor.Tools.blockTools.get(name); const tool = this.Editor.Tools.blockTools.get(name);
const actialData = await this.composeBlockData(tool, data);
const block = new Block({ const block = new Block({
id, id,
data, data: actialData,
tool, tool,
api: this.Editor.API, api: this.Editor.API,
readOnly, readOnly,
tunesData, tunesData,
}); });
if (!readOnly) { if (!readOnly) {
this.bindBlockEvents(block); this.bindBlockEvents(block);
} }
@ -269,7 +270,7 @@ export default class BlockManager extends Module {
* *
* @returns {Block} * @returns {Block}
*/ */
public insert({ public async insert({
id = undefined, id = undefined,
tool = this.config.defaultBlock, tool = this.config.defaultBlock,
data = {}, data = {},
@ -284,15 +285,14 @@ export default class BlockManager extends Module {
index?: number; index?: number;
needToFocus?: boolean; needToFocus?: boolean;
replace?: boolean; replace?: boolean;
tunes?: {[name: string]: BlockTuneData}; tunes?: { [name: string]: BlockTuneData };
} = {}): Block { } = {}): Promise<Block> {
let newIndex = index; let newIndex = index;
if (newIndex === undefined) { if (newIndex === undefined) {
newIndex = this.currentBlockIndex + (replace ? 0 : 1); newIndex = this.currentBlockIndex + (replace ? 0 : 1);
} }
const block = await this.composeBlock({
const block = this.composeBlock({
id, id,
tool, tool,
data, data,
@ -339,7 +339,7 @@ export default class BlockManager extends Module {
public replace({ public replace({
tool = this.config.defaultBlock, tool = this.config.defaultBlock,
data = {}, data = {},
}): Block { }): Promise<Block> {
return this.insert({ return this.insert({
tool, tool,
data, data,
@ -355,12 +355,12 @@ export default class BlockManager extends Module {
* @param {PasteEvent} pasteEvent - pasted data * @param {PasteEvent} pasteEvent - pasted data
* @param {boolean} replace - should replace current block * @param {boolean} replace - should replace current block
*/ */
public paste( public async paste(
toolName: string, toolName: string,
pasteEvent: PasteEvent, pasteEvent: PasteEvent,
replace = false replace = false
): Block { ): Promise<Block> {
const block = this.insert({ const block = await this.insert({
tool: toolName, tool: toolName,
replace, replace,
}); });
@ -384,8 +384,8 @@ export default class BlockManager extends Module {
* *
* @returns {Block} inserted Block * @returns {Block} inserted Block
*/ */
public insertDefaultBlockAtIndex(index: number, needToFocus = false): Block { public async insertDefaultBlockAtIndex(index: number, needToFocus = false): Promise<Block> {
const block = this.composeBlock({ tool: this.config.defaultBlock }); const block = await this.composeBlock({ tool: this.config.defaultBlock });
this._blocks[index] = block; this._blocks[index] = block;
@ -410,7 +410,7 @@ export default class BlockManager extends Module {
* *
* @returns {Block} * @returns {Block}
*/ */
public insertAtEnd(): Block { public insertAtEnd(): Promise<Block> {
/** /**
* Define new value for current block index * Define new value for current block index
*/ */
@ -534,7 +534,7 @@ export default class BlockManager extends Module {
* *
* @returns {Block} * @returns {Block}
*/ */
public split(): Block { public split(): Promise<Block> {
const extractedFragment = this.Editor.Caret.extractFragmentFromCaretPosition(); const extractedFragment = this.Editor.Caret.extractFragmentFromCaretPosition();
const wrapper = $.make('div'); const wrapper = $.make('div');
@ -881,4 +881,24 @@ export default class BlockManager extends Module {
return block; return block;
} }
/**
* Retrieves default block data by creating fake block.
* Merges retrieved data with specified data object.
*
* @param tool - block's tool
* @param dataOverrides - object containing overrides for default block data
*/
private async composeBlockData(tool: BlockTool, dataOverrides = {}): Promise<BlockToolData> {
const block = new Block({
tool,
api: this.Editor.API,
readOnly: true,
data: {},
tunesData: {},
});
const blockData = await block.data;
return Object.assign(blockData, dataOverrides);
}
} }

View file

@ -336,9 +336,9 @@ export default class Caret extends Module {
if (lastBlock.tool.isDefault && lastBlock.isEmpty) { if (lastBlock.tool.isDefault && lastBlock.isEmpty) {
this.setToBlock(lastBlock); this.setToBlock(lastBlock);
} else { } else {
const newBlock = this.Editor.BlockManager.insertAtEnd(); this.Editor.BlockManager.insertAtEnd().then(newBlock => {
this.setToBlock(newBlock);
this.setToBlock(newBlock); });
} }
} }
@ -390,7 +390,7 @@ export default class Caret extends Module {
* *
* @returns {boolean} * @returns {boolean}
*/ */
public navigateNext(): boolean { public async navigateNext(): Promise<boolean> {
const { BlockManager } = this.Editor; const { BlockManager } = this.Editor;
const { currentBlock, nextContentfulBlock } = BlockManager; const { currentBlock, nextContentfulBlock } = BlockManager;
const { nextInput } = currentBlock; const { nextInput } = currentBlock;
@ -417,7 +417,7 @@ export default class Caret extends Module {
* If there is no nextBlock, but currentBlock is not default, * If there is no nextBlock, but currentBlock is not default,
* insert new default block at the end and navigate to it * insert new default block at the end and navigate to it
*/ */
nextBlock = BlockManager.insertAtEnd(); nextBlock = await BlockManager.insertAtEnd();
} }
if (isAtEnd) { if (isAtEnd) {

View file

@ -6,7 +6,6 @@ import {
PasteEvent, PasteEvent,
PasteEventDetail PasteEventDetail
} from '../../../types'; } from '../../../types';
import Block from '../block';
import { SavedData } from '../../../types/data-formats'; import { SavedData } from '../../../types/data-formats';
import { clean, sanitizeBlocks } from '../utils/sanitizer'; import { clean, sanitizeBlocks } from '../utils/sanitizer';
import BlockTool from '../tools/block'; import BlockTool from '../tools/block';
@ -112,12 +111,12 @@ export default class Paste extends Module {
/** /**
* Tags` substitutions parameters * Tags` substitutions parameters
*/ */
private toolsTags: {[tag: string]: TagSubstitute} = {}; private toolsTags: { [tag: string]: TagSubstitute } = {};
/** /**
* Store tags to substitute by tool name * Store tags to substitute by tool name
*/ */
private tagsByTool: {[tools: string]: string[]} = {}; private tagsByTool: { [tools: string]: string[] } = {};
/** Patterns` substitutions parameters */ /** Patterns` substitutions parameters */
private toolsPatterns: PatternSubstitute[] = []; private toolsPatterns: PatternSubstitute[] = [];
@ -186,7 +185,7 @@ export default class Paste extends Module {
this.insertEditorJSData(JSON.parse(editorJSData)); this.insertEditorJSData(JSON.parse(editorJSData));
return; return;
} catch (e) {} // Do nothing and continue execution as usual if error appears } catch (e) { } // Do nothing and continue execution as usual if error appears
} }
/** /**
@ -449,7 +448,7 @@ export default class Paste extends Module {
private async processFiles(items: FileList): Promise<void> { private async processFiles(items: FileList): Promise<void> {
const { BlockManager } = this.Editor; const { BlockManager } = this.Editor;
let dataToInsert: {type: string; event: PasteEvent}[]; let dataToInsert: { type: string; event: PasteEvent }[];
dataToInsert = await Promise.all( dataToInsert = await Promise.all(
Array Array
@ -473,7 +472,7 @@ export default class Paste extends Module {
* *
* @param {File} file - file to process * @param {File} file - file to process
*/ */
private async processFile(file: File): Promise<{event: PasteEvent; type: string}> { private async processFile(file: File): Promise<{ event: PasteEvent; type: string }> {
const extension = _.getFileExtension(file); const extension = _.getFileExtension(file);
const foundConfig = Object const foundConfig = Object
@ -576,7 +575,7 @@ export default class Paste extends Module {
* @returns {PasteData[]} * @returns {PasteData[]}
*/ */
private processPlain(plain: string): PasteData[] { private processPlain(plain: string): PasteData[] {
const { defaultBlock } = this.config as {defaultBlock: string}; const { defaultBlock } = this.config as { defaultBlock: string };
if (!plain) { if (!plain) {
return []; return [];
@ -652,9 +651,9 @@ export default class Paste extends Module {
BlockManager.currentBlock.tool.isDefault && BlockManager.currentBlock.tool.isDefault &&
BlockManager.currentBlock.isEmpty; BlockManager.currentBlock.isEmpty;
const insertedBlock = BlockManager.paste(blockData.tool, blockData.event, needToReplaceCurrentBlock); BlockManager.paste(blockData.tool, blockData.event, needToReplaceCurrentBlock).then(insertedBlock => {
Caret.setToBlock(insertedBlock, Caret.positions.END);
Caret.setToBlock(insertedBlock, Caret.positions.END); });
return; return;
} }
@ -681,7 +680,7 @@ export default class Paste extends Module {
* *
* @returns {Promise<{event: PasteEvent, tool: string}>} * @returns {Promise<{event: PasteEvent, tool: string}>}
*/ */
private async processPattern(text: string): Promise<{event: PasteEvent; tool: string}> { private async processPattern(text: string): Promise<{ event: PasteEvent; tool: string }> {
const pattern = this.toolsPatterns.find((substitute) => { const pattern = this.toolsPatterns.find((substitute) => {
const execResult = substitute.pattern.exec(text); const execResult = substitute.pattern.exec(text);
@ -718,18 +717,16 @@ export default class Paste extends Module {
private insertBlock(data: PasteData, canReplaceCurrentBlock = false): void { private insertBlock(data: PasteData, canReplaceCurrentBlock = false): void {
const { BlockManager, Caret } = this.Editor; const { BlockManager, Caret } = this.Editor;
const { currentBlock } = BlockManager; const { currentBlock } = BlockManager;
let block: Block;
if (canReplaceCurrentBlock && currentBlock && currentBlock.isEmpty) { if (canReplaceCurrentBlock && currentBlock && currentBlock.isEmpty) {
block = BlockManager.paste(data.tool, data.event, true); BlockManager.paste(data.tool, data.event, true).then(block => {
Caret.setToBlock(block, Caret.positions.END); Caret.setToBlock(block, Caret.positions.END);
});
return; } else {
BlockManager.paste(data.tool, data.event).then(block => {
Caret.setToBlock(block, Caret.positions.END);
});
} }
block = BlockManager.paste(data.tool, data.event);
Caret.setToBlock(block, Caret.positions.END);
} }
/** /**
@ -754,13 +751,13 @@ export default class Paste extends Module {
needToReplaceCurrentBlock = isCurrentBlockDefault && BlockManager.currentBlock.isEmpty; needToReplaceCurrentBlock = isCurrentBlockDefault && BlockManager.currentBlock.isEmpty;
} }
const block = BlockManager.insert({ BlockManager.insert({
tool, tool,
data, data,
replace: needToReplaceCurrentBlock, replace: needToReplaceCurrentBlock,
}).then(block => {
Caret.setToBlock(block, Caret.positions.END);
}); });
Caret.setToBlock(block, Caret.positions.END);
}); });
} }

View file

@ -77,7 +77,7 @@ export default class Renderer extends Module {
if (Tools.available.has(tool)) { if (Tools.available.has(tool)) {
try { try {
BlockManager.insert({ await BlockManager.insert({
id, id,
tool, tool,
data, data,
@ -105,12 +105,11 @@ export default class Renderer extends Module {
stubData.title = toolboxTitle || stubData.title; stubData.title = toolboxTitle || stubData.title;
} }
const stub = BlockManager.insert({ const stub = await BlockManager.insert({
id, id,
tool: Tools.stubTool, tool: Tools.stubTool,
data: stubData, data: stubData,
}); })
stub.stretched = true; stub.stretched = true;
_.log(`Tool «${tool}» is not found. Check 'tools' property at your initial Editor.js config.`, 'warn'); _.log(`Tool «${tool}» is not found. Check 'tools' property at your initial Editor.js config.`, 'warn');

View file

@ -514,19 +514,21 @@ export default class UI extends Module<UINodes> {
if (BlockSelection.anyBlockSelected && !Selection.isSelectionExists) { if (BlockSelection.anyBlockSelected && !Selection.isSelectionExists) {
const selectionPositionIndex = BlockManager.removeSelectedBlocks(); const selectionPositionIndex = BlockManager.removeSelectedBlocks();
Caret.setToBlock(BlockManager.insertDefaultBlockAtIndex(selectionPositionIndex, true), Caret.positions.START); BlockManager.insertDefaultBlockAtIndex(selectionPositionIndex, true).then(block => {
Caret.setToBlock(block, Caret.positions.START);
/** Clear selection */ /** Clear selection */
BlockSelection.clearSelection(event); BlockSelection.clearSelection(event);
/** /**
* Stop propagations * Stop propagations
* Manipulation with BlockSelections is handled in global backspacePress because they may occur * Manipulation with BlockSelections is handled in global backspacePress because they may occur
* with CMD+A or RectangleSelection and they can be handled on document event * with CMD+A or RectangleSelection and they can be handled on document event
*/ */
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
});
} }
} }
@ -596,19 +598,19 @@ export default class UI extends Module<UINodes> {
/** /**
* Insert the default typed Block * Insert the default typed Block
*/ */
const newBlock = this.Editor.BlockManager.insert(); this.Editor.BlockManager.insert().then(newBlock => {
this.Editor.Caret.setToBlock(newBlock);
this.Editor.Caret.setToBlock(newBlock); /**
* And highlight
*/
this.Editor.BlockManager.highlightCurrentNode();
/** /**
* And highlight * Move toolbar and show plus button because new Block is empty
*/ */
this.Editor.BlockManager.highlightCurrentNode(); this.Editor.Toolbar.moveAndOpen(newBlock);
});
/**
* Move toolbar and show plus button because new Block is empty
*/
this.Editor.Toolbar.moveAndOpen(newBlock);
} }
this.Editor.BlockSelection.clearSelection(event); this.Editor.BlockSelection.clearSelection(event);

View file

@ -379,7 +379,7 @@ export default class Toolbox extends EventsDispatcher<ToolboxEvent> {
* @param {string} toolName - Tool name * @param {string} toolName - Tool name
* @param blockDataOverrides - predefined Block data * @param blockDataOverrides - predefined Block data
*/ */
private insertNewBlock(toolName: string, blockDataOverrides?: BlockToolData): void { private async insertNewBlock(toolName: string, blockDataOverrides?: BlockToolData): Promise<void> {
const currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); const currentBlockIndex = this.api.blocks.getCurrentBlockIndex();
const currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex); const currentBlock = this.api.blocks.getBlockByIndex(currentBlockIndex);
@ -393,7 +393,7 @@ export default class Toolbox extends EventsDispatcher<ToolboxEvent> {
*/ */
const index = currentBlock.isEmpty ? currentBlockIndex : currentBlockIndex + 1; const index = currentBlock.isEmpty ? currentBlockIndex : currentBlockIndex + 1;
const newBlock = this.api.blocks.insert( const newBlock = await this.api.blocks.insert(
toolName, toolName,
blockDataOverrides, blockDataOverrides,
undefined, undefined,

View file

@ -65,8 +65,7 @@ describe('api.blocks', () => {
const newBlockData = { const newBlockData = {
text: 'Updated text', text: 'Updated text',
}; };
await editor.blocks.update(idToUpdate, newBlockData);
editor.blocks.update(idToUpdate, newBlockData);
cy.get('[data-cy=editorjs]') cy.get('[data-cy=editorjs]')
.get('div.ce-block') .get('div.ce-block')
@ -86,12 +85,10 @@ describe('api.blocks', () => {
const newBlockData = { const newBlockData = {
text: 'Updated text', text: 'Updated text',
}; };
await editor.blocks.update(idToUpdate, newBlockData);
editor.blocks.update(idToUpdate, newBlockData);
const output = await (editor as any).save(); const output = await (editor as any).save();
const text = output.blocks[0].data.text; const text = output.blocks[0].data.text;
expect(text).to.be.eq(newBlockData.text); expect(text).to.be.eq(newBlockData.text);
}); });
}); });

View file

@ -110,7 +110,7 @@ export interface Blocks {
index?: number, index?: number,
needToFocus?: boolean, needToFocus?: boolean,
replace?: boolean, replace?: boolean,
): BlockAPI; ): Promise<BlockAPI>;
/** /**
@ -119,5 +119,5 @@ export interface Blocks {
* @param id - id of the block to update * @param id - id of the block to update
* @param data - the new data * @param data - the new data
*/ */
update(id: string, data: BlockToolData): void; update(id: string, data: BlockToolData): Promise<void>;
} }