New block lifecycle hook and BlockToolAPI: "moved" (#1007)

* Update CHANGELOG.md

* feat: add swapped lifecycle hook

* feat: deprecate swap and add move instead

* feat: set fromIndex by default, add MoveEvent, add indices checks in blockManager

* refactor: MoveEvent interface more event-like; docs: added docs for MoveEvent

* fix asterix alignment due to tslint error

* fix tslint

* extending CustomEvent

* update bundle

* Update CHANGELOG.md

Co-authored-by: Peter Savchenko <specc.dev@gmail.com>
Co-authored-by: Murod Khaydarov <murod.haydarov@gmail.com>
This commit is contained in:
Stefan Natter 2020-03-14 21:32:01 +01:00 committed by GitHub
parent 3877380ee1
commit 8b838bf8aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 172 additions and 16 deletions

2
dist/editor.js vendored

File diff suppressed because one or more lines are too long

View file

@ -10,6 +10,8 @@
- `Fix` - Fix block-tune buttons alignment in some CSS-resetors that forces `box-sizing: border-box` rule [#1003](https://github.com/codex-team/editor.js/issues/1003)
- `Improvements` - New style of a Block Settings button. Focused block background removed.
- `New` — Add in-house copy-paste support through `application/x-editor-js` mime-type
- `New` Block [lifecycle hook](tools.md#block-lifecycle-hooks) `moved`
- `Deprecated` — [`blocks.swap(fromIndex, toIndex)`](api.md) method is deprecated. Use `blocks.move(toIndex, fromIndex)` instead.
### 2.16.1

View file

@ -26,7 +26,11 @@ Methods that working with Blocks
`renderFromHTML(data)` - parse and render passed HTML string (*not for production use*)
`swap(fromIndex, toIndex)` - swaps two Blocks by their positions
`swap(fromIndex, toIndex)` - swaps two Blocks by their positions (deprecated:
use 'move' instead)
`move(toIndex, fromIndex)` - moves block from one index to another position.
`fromIndex` will be the current block's index by default.
`delete(blockIndex?: Number)` - deletes Block with passed index

View file

@ -476,3 +476,8 @@ Called each time Block contents is updated
### `removed()`
Called after Block contents is removed from the page but before Block instance deleted
### `moved(MoveEvent)`
Called after Block was moved. `MoveEvent` contains `fromIndex` and `toIndex`
respectively.

View file

@ -92,7 +92,7 @@ export default class MoveDownTune implements BlockTune {
window.scrollTo(0, scrollOffset);
/** Change blocks positions */
this.api.blocks.swap(currentBlockIndex, currentBlockIndex + 1);
this.api.blocks.move(currentBlockIndex + 1);
/** Hide the Tooltip */
this.api.tooltip.hide();

View file

@ -46,7 +46,7 @@ export default class MoveUpTune implements BlockTune {
'click',
(event) => this.handleClick(event as MouseEvent, moveUpButton),
false,
);
);
/**
* Enable tooltip module on button
@ -99,7 +99,7 @@ export default class MoveUpTune implements BlockTune {
window.scrollBy(0, -1 * scrollUpOffset);
/** Change blocks positions */
this.api.blocks.swap(currentBlockIndex, currentBlockIndex - 1);
this.api.blocks.move(currentBlockIndex - 1);
/** Hide the Tooltip */
this.api.tooltip.hide();

View file

@ -38,6 +38,7 @@ export enum BlockToolAPI {
*/
APPEND_CALLBACK = 'appendCallback',
RENDERED = 'rendered',
MOVED = 'moved',
UPDATED = 'updated',
REMOVED = 'removed',
ON_PASTE = 'onPaste',
@ -441,7 +442,7 @@ export default class Block {
* @param {Object} data
*/
public async mergeWith(data: BlockToolData): Promise<void> {
await this.tool.merge(data);
await this.tool.merge(data);
}
/**
* Extracts data from Block
@ -465,7 +466,7 @@ export default class Block {
return {
tool: this.name,
data: finishedExtraction,
time : measuringEnd - measuringStart,
time: measuringEnd - measuringStart,
};
})
.catch((error) => {
@ -562,7 +563,7 @@ export default class Block {
private compose(): HTMLDivElement {
const wrapper = $.make('div', Block.CSS.wrapper) as HTMLDivElement,
contentNode = $.make('div', Block.CSS.content),
pluginsContent = this.tool.render();
pluginsContent = this.tool.render();
contentNode.appendChild(pluginsContent);
wrapper.appendChild(contentNode);

View file

@ -1,6 +1,7 @@
import * as _ from './utils';
import $ from './dom';
import Block, {BlockToolAPI} from './block';
import Block, { BlockToolAPI } from './block';
import {MoveEvent, MoveEventDetail} from '../../types/tools';
/**
* @class Blocks
@ -127,6 +128,7 @@ export default class Blocks {
* Swaps blocks with indexes first and second
* @param {Number} first - first block index
* @param {Number} second - second block index
* @deprecated use 'move' instead
*/
public swap(first: number, second: number): void {
const secondBlock = this.blocks[second];
@ -143,6 +145,42 @@ export default class Blocks {
this.blocks[first] = secondBlock;
}
/**
* Move a block from one to another index
* @param {Number} toIndex - new index of the block
* @param {Number} fromIndex - block to move
*/
public move(toIndex: number, fromIndex: number): void {
/**
* cut out the block, move the DOM element and insert at the desired index
* again (the shifting within the blocks array will happen automatically).
* @see https://stackoverflow.com/a/44932690/1238150
*/
const block = this.blocks.splice(fromIndex, 1)[0];
// manipulate DOM
const prevIndex = toIndex - 1;
const previousBlockIndex = Math.max(0, prevIndex);
const previousBlock = this.blocks[previousBlockIndex];
if (toIndex > 0) {
this.insertToDOM(block, 'afterend', previousBlock);
} else {
this.insertToDOM(block, 'beforebegin', previousBlock);
}
// move in array
this.blocks.splice(toIndex, 0, block);
// invoke hook
const event: MoveEvent = this.composeBlockEvent('move', {
fromIndex,
toIndex,
});
block.call(BlockToolAPI.MOVED, event);
}
/**
* Insert new Block at passed index
*
@ -261,4 +299,17 @@ export default class Blocks {
block.call(BlockToolAPI.RENDERED);
}
/**
* Composes Block event with passed type and details
*
* @param {String} type
* @param {MoveEventDetail} detail
*/
private composeBlockEvent(type: string, detail: MoveEventDetail): MoveEvent {
return new CustomEvent(type, {
detail,
},
) as MoveEvent;
}
}

View file

@ -130,6 +130,7 @@ export default class Dom {
* Swap two elements in parent
* @param {HTMLElement} el1 - from
* @param {HTMLElement} el2 - to
* @deprecated
*/
public static swap(el1: HTMLElement, el2: HTMLElement): void {
// create marker element and insert it where el1 is

View file

@ -20,6 +20,7 @@ export default class BlocksAPI extends Module {
renderFromHTML: (data: string) => this.renderFromHTML(data),
delete: () => this.delete(),
swap: (fromIndex: number, toIndex: number) => this.swap(fromIndex, toIndex),
move: (toIndex: number, fromIndex?: number) => this.move(toIndex, fromIndex),
getBlockByIndex: (index: number) => this.getBlockByIndex(index),
getCurrentBlockIndex: () => this.getCurrentBlockIndex(),
getBlocksCount: () => this.getBlocksCount(),
@ -60,6 +61,7 @@ export default class BlocksAPI extends Module {
* Call Block Manager method that swap Blocks
* @param {number} fromIndex - position of first Block
* @param {number} toIndex - position of second Block
* @deprecated use 'move' instead
*/
public swap(fromIndex: number, toIndex: number): void {
this.Editor.BlockManager.swap(fromIndex, toIndex);
@ -71,6 +73,21 @@ export default class BlocksAPI extends Module {
this.Editor.Toolbar.move(false);
}
/**
* Move block from one index to another
* @param {Number} toIndex
* @param {number} fromIndex
*/
public move(toIndex: number, fromIndex?: number): void {
this.Editor.BlockManager.move(toIndex, fromIndex);
/**
* Move toolbar
* DO not close the settings
*/
this.Editor.Toolbar.move(false);
}
/**
* Deletes Block
* @param blockIndex

View file

@ -552,6 +552,7 @@ export default class BlockManager extends Module {
* Swap Blocks Position
* @param {Number} fromIndex
* @param {Number} toIndex
* @deprecated use 'move' instead
*/
public swap(fromIndex, toIndex): void {
/** Move up current Block */
@ -561,6 +562,30 @@ export default class BlockManager extends Module {
this.currentBlockIndex = toIndex;
}
/**
* Move a block to a new index
* @param {Number} toIndex
* @param {Number} fromIndex
*/
public move(toIndex, fromIndex = this.currentBlockIndex): void {
// make sure indexes are valid and within a valid range
if (isNaN(toIndex) || isNaN(fromIndex)) {
_.log(`Warning during 'move' call: incorrect indices provided.`, 'warn');
return;
}
if (!this.validateIndex(toIndex) || !this.validateIndex(fromIndex)) {
_.log(`Warning during 'move' call: indices cannot be lower than 0 or greater than the amount of blocks.`, 'warn');
return;
}
/** Move up current Block */
this._blocks.move(toIndex, fromIndex);
/** Now actual block moved so that current block index changed */
this.currentBlockIndex = toIndex;
}
/**
* Sets current Block Index -1 which means unknown
* and clear highlightings
@ -604,4 +629,16 @@ export default class BlockManager extends Module {
Listeners.on(block.holder, 'dragover', (event) => BlockEvents.dragOver(event as DragEvent));
Listeners.on(block.holder, 'dragleave', (event) => BlockEvents.dragLeave(event as DragEvent));
}
/**
* Validates that the given index is not lower than 0 or higher than the amount of blocks
* @param {number} index - index of blocks array to validate
*/
private validateIndex(index: number): boolean {
if (index < 0 || index >= this._blocks.length) {
return false;
}
return true;
}
}

View file

@ -33,9 +33,17 @@ export interface Blocks {
* Swaps two Blocks
* @param {number} fromIndex - block to swap
* @param {number} toIndex - block to swap with
* @deprecated use 'move' instead
*/
swap(fromIndex: number, toIndex: number): void;
/**
* Moves a block to a new index
* @param {number} toIndex - index where the block is moved to
* @param {number} fromIndex - block to move
*/
move(toIndex: number, fromIndex?: number): void;
/**
* Returns Block holder by Block index
* @param {number} index

View file

@ -1,9 +1,10 @@
import {ConversionConfig, PasteConfig, SanitizerConfig} from '../configs';
import {BlockToolData} from './block-tool-data';
import {BaseTool, BaseToolConstructable} from './tool';
import {ToolConfig} from './tool-config';
import {API} from '../index';
import {PasteEvent} from './paste-events';
import { ConversionConfig, PasteConfig, SanitizerConfig } from '../configs';
import { BlockToolData } from './block-tool-data';
import { BaseTool, BaseToolConstructable } from './tool';
import { ToolConfig } from './tool-config';
import { API } from '../index';
import { PasteEvent } from './paste-events';
import { MoveEvent } from './hook-events';
/**
* Describe Block Tool object
* @see {@link docs/tools.md}
@ -65,6 +66,11 @@ export interface BlockTool extends BaseTool {
* Called after block removed from the page but before instance is deleted
*/
removed?(): void;
/**
* Called after block was moved
*/
moved?(event: MoveEvent): void;
}
export interface BlockToolConstructable extends BaseToolConstructable {
@ -97,5 +103,5 @@ export interface BlockToolConstructable extends BaseToolConstructable {
* @constructor
* @return {BlockTool}
*/
new (config: {api: API, config: ToolConfig, data: BlockToolData}): BlockTool;
new(config: { api: API, config: ToolConfig, data: BlockToolData }): BlockTool;
}

23
types/tools/hook-events.d.ts vendored Normal file
View file

@ -0,0 +1,23 @@
/**
* Event detail for block relocation
*/
export interface MoveEventDetail {
/**
* index the block was moved from
*/
fromIndex: number;
/**
* index the block was moved to
*/
toIndex: number;
}
/**
* Move event for block relocation
*/
export interface MoveEvent extends CustomEvent {
/**
* Override detail property of CustomEvent by MoveEvent hook
*/
readonly detail: MoveEventDetail;
}

View file

@ -9,6 +9,7 @@ export * from './tool';
export * from './tool-config';
export * from './tool-settings';
export * from './paste-events';
export * from './hook-events';
export type Tool = BaseTool | BlockTool | InlineTool;
export type ToolConstructable = BaseToolConstructable | BlockToolConstructable | InlineToolConstructable;