Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] In-house copy paste #1060

Merged
merged 18 commits into from
Mar 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dist/editor.js

Large diffs are not rendered by default.

56 changes: 28 additions & 28 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@

### 2.17

- `Improvements` - Editor's [onchange callback](https://editorjs.io/configuration#editor-modifications-callback) now accepts an API as a parameter
- `Fix` - Some mistakes are fixed in [installation.md](installation.md)
- `Improvements` - Editor's [onchange callback](https://editorjs.io/configuration#editor-modifications-callback) now accepts an API as a parameter
- `Fix` - Some mistakes are fixed in [installation.md](installation.md)
- `Fix` - Fixed multiple paste callback triggering in a case when several editors are instantiated [#1011](https://github.com/codex-team/editor.js/issues/1011)
- `Fix` - Fixed inline toolbar flipper activation on closing conversion toolbar [#995](https://github.com/codex-team/editor.js/issues/995)
- `Fix` - Fixed inline toolbar flipper activation on closing conversion toolbar [#995](https://github.com/codex-team/editor.js/issues/995)
- `Improvements` - New window tab is opened by clicking on anchor with ctrl [#1057](https://github.com/codex-team/editor.js/issues/1057)
- `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.
- `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

### 2.16.1

- `Fix` — Fix Firefox bug with incorrect height and cursor position of empty content editable elements [#947](https://github.com/codex-team/editor.js/issues/947) [#876](https://github.com/codex-team/editor.js/issues/876) [#608](https://github.com/codex-team/editor.js/issues/608) [#876](https://github.com/codex-team/editor.js/issues/876)
- `Fix` — Set initial hidden Inline Toolbar position [#979](https://github.com/codex-team/editor.js/issues/979)
- `Fix` — Fix issue with CodeX.Toolips TypeScript definitions [#978](https://github.com/codex-team/editor.js/issues/978)
- `Fix` — Set initial hidden Inline Toolbar position [#979](https://github.com/codex-team/editor.js/issues/979)
- `Fix` — Fix issue with CodeX.Toolips TypeScript definitions [#978](https://github.com/codex-team/editor.js/issues/978)
- `Fix` — Fix some issues with Inline and Tunes toolbars.
- `Fix` - Fix `minHeight` option with zero-value issue [#724](https://github.com/codex-team/editor.js/issues/724)
- `Improvements` — Disable Conversion Toolbar if there are no Tools to convert [#984](https://github.com/codex-team/editor.js/issues/984)
- `Improvements` — Disable Conversion Toolbar if there are no Tools to convert [#984](https://github.com/codex-team/editor.js/issues/984)

### 2.16

- `Improvements` — Inline Toolbar design improved
- `Improvements` — Conversion Toolbar now included in the Inline Toolbar [#853](https://github.com/codex-team/editor.js/issues/853)
- `Improvements` — All buttons now have beautiful Tooltips provided by [CodeX Tooltips](https://github.com/codex-team/codex.tooltips)
- `New` — new Tooltips API for displaying tooltips near your custom elements
`New` *API* — Block [lifecycle hooks](tools.md#block-lifecycle-hooks)
`New` *Inline Tools API* — Ability to specify Tool's title via `title` static getter.
- `New` — new Tooltips API for displaying tooltips near your custom elements
- `New` *API* — Block [lifecycle hooks](tools.md#block-lifecycle-hooks)
- `New` *Inline Tools API* — Ability to specify Tool's title via `title` static getter.
- `Fix` — On selection from end to start backspace is working as expected now [#869](https://github.com/codex-team/editor.js/issues/869)
`Fix` — Fix flipper with empty dom iterator [#926](https://github.com/codex-team/editor.js/issues/926)
- `Fix` — Fix flipper with empty dom iterator [#926](https://github.com/codex-team/editor.js/issues/926)
- `Fix` — Normalize node before walking through children at `isEmpty` method [#943](https://github.com/codex-team/editor.js/issues/943)
— `Fix` — Fixed Grammarly conflict [#779](https://github.com/codex-team/editor.js/issues/779)
— `Improvements` — Module Listeners now correctly removes events with options [#904](https://github.com/codex-team/editor.js/pull/904)
— `Improvements` — Styles API: `.cdx-block` default vertical margins decreased from 0.7 to 0.4 ems.
— `Fix` — Fixed History Back on block deletion by Backspace in Firefox [#967](https://github.com/codex-team/editor.js/pull/967)
- `Fix` — Fixed Grammarly conflict [#779](https://github.com/codex-team/editor.js/issues/779)
- `Improvements` — Module Listeners now correctly removes events with options [#904](https://github.com/codex-team/editor.js/pull/904)
- `Improvements` — Styles API: `.cdx-block` default vertical margins decreased from 0.7 to 0.4 ems.
- `Fix` — Fixed `getRangeCount` call if range count is 0 [#938](https://github.com/codex-team/editor.js/issues/938)
- `New` — Log levels now available to suppress Editor.js console messages [#962](https://github.com/codex-team/editor.js/issues/962)
- `New` — Log levels now available to suppress Editor.js console messages [#962](https://github.com/codex-team/editor.js/issues/962)
- `Fix` — Fixed wrong navigation on block deletion

### 2.15.1
Expand All @@ -48,10 +48,10 @@

### 2.15

- `New` — New [`blocks.insert()`](api.md) API method [#715](https://github.com/codex-team/editor.js/issues/715).
- `New` — New [`blocks.insert()`](api.md) API method [#715](https://github.com/codex-team/editor.js/issues/715).
- `New` *Conversion Toolbar* — Ability to convert one block to another [#704](https://github.com/codex-team/editor.js/issues/704)
- `New` *Cross-block selection* — Ability to select multiple blocks by mouse and with SHIFT+ARROWS [#703](https://github.com/codex-team/editor.js/issues/703)
- `Deprecated` — [`blocks.insertNewBlock()`](api.md) method is deprecated. Use `blocks.insert()` instead.
- `Deprecated` — [`blocks.insertNewBlock()`](api.md) method is deprecated. Use `blocks.insert()` instead.
- `Improvements` — Inline Toolbar now works on mobile devices [#706](https://github.com/codex-team/editor.js/issues/706)
- `Improvements` — Toolbar looks better on mobile devices [#706](https://github.com/codex-team/editor.js/issues/706)
- `Improvements` — Now `pasteConfig` can return `false` to disable paste handling on your Tool [#801](https://github.com/codex-team/editor.js/issues/801)
Expand All @@ -77,9 +77,9 @@
- `Deprecated` *Config* - `holderId` property now is deprecated and will removed in next major release. Use `holder` instead.
- `Fix` *Types* — Fixed error with `codex-notifier` package [#713](https://github.com/codex-team/editor.js/issues/713)
- `Improvements` — Close inline toolbar after creating a new link.
- `New` *Config* — Option `minHeight` for customizing Editor's bottom zone height added.
- `New` *Config* — Option `minHeight` for customizing Editor's bottom zone height added.

### 2.12.4
### 2.12.4

- `Improvements` — CodeX.Shortcuts version updated to the v1.1 [#684](https://github.com/codex-team/editor.js/issues/684)
- `Fix` — Do not start multi-block selection on Toolbox and Inline Toolbar [#646](https://github.com/codex-team/editor.js/issues/646)
Expand All @@ -92,15 +92,15 @@

### 2.12.2

- New *Inline Tools* — pass tool settings from configuration to Tool constructor
- New *Inline Tools* — pass tool settings from configuration to Tool constructor

### 2.12.1

- `Fix` — Fix processing `color-mod` function in styles

### 2.12.0
### 2.12.0

- `New` *API* - new `blocks` API method `renderFromHTML`
- `New` *API* - new `blocks` API method `renderFromHTML`

### 2.11.11

Expand All @@ -112,7 +112,7 @@

### 2.11.9

- `Fix` - Fix inline toolbar buttons margin. Update dependencies list. Update tools for example page.
- `Fix` - Fix inline toolbar buttons margin. Update dependencies list. Update tools for example page.

### 2.11.8

Expand Down Expand Up @@ -202,7 +202,7 @@

- `Fix` — cmd+x works only for custom selection now

### 2.7.28
### 2.7.28

- `New` [Tools Validation](https://github.com/codex-team/editor.js/blob/master/docs/tools.md#validate-optional) is added.

Expand Down Expand Up @@ -232,9 +232,9 @@
- `New` *Sanitize API* — [Sanitize Config](https://github.com/codex-team/editor.js/blob/master/docs/tools.md#automatic-sanitize) of `Block Tools` now automatically extends by tags of `Inline Tools` that is enabled by current Tool by `inlineToolbar` option. You don't need more to specify `a, b, mark, code` manually. This feature will be added to fields that supports inline markup.
- `New` *Block Selection* — Ability to select Block by `CMD+A`, and the whole Editor by double `CMD+A`. After that, you can copy (`CMD+C`), remove (`Backspace`) or clear (`Enter`) selected Blocks.
- `New` *[Styles API](https://github.com/codex-team/editor.js/blob/master/types/api/styles.d.ts)* — Added `button` class for stylization of any buttons provided by Tools with one unified style.
- `New` *[Notifier API](https://github.com/codex-team/editor.js/blob/master/docs/api.md#notifierapi)* — methods for showing user notifications: on success, errors, warnings, etc.
- `New` *Block Tool* — [Table](http://github.com/editor-js/table) constructor 💪
- `New` If one of the Tools is unavailable on Editor initialization, its Blocks will be rendered with *Dummy Block*, describing that user can not edit content of this Block. Dummy Blocks can be moved, removed and saved as normal Blocks. So saved data won't be lost if one of the Tools is failed
- `New` *[Notifier API](https://github.com/codex-team/editor.js/blob/master/docs/api.md#notifierapi)* — methods for showing user notifications: on success, errors, warnings, etc.
- `New` *Block Tool* — [Table](http://github.com/editor-js/table) constructor 💪
- `New` If one of the Tools is unavailable on Editor initialization, its Blocks will be rendered with *Dummy Block*, describing that user can not edit content of this Block. Dummy Blocks can be moved, removed and saved as normal Blocks. So saved data won't be lost if one of the Tools is failed
- `New` [Public TS-types](https://github.com/codex-team/editor.js/tree/master/types) are presented.
- `Changes` *Tools API* — options `irreplaceable` and `contentless` was removed.
- `Changes` *Tools API* — [Paste API](https://github.com/codex-team/editor.js/blob/master/docs/tools.md#paste-handling): tags, patterns and mime-types now should be specified by Tool's `pasteConfig` static property. Custom Paste Event should be handled by `onPaste(event)` that should not be static from now.
Expand Down
1 change: 1 addition & 0 deletions src/components/external/codex.tooltips
Submodule codex.tooltips added at 72a7c0
27 changes: 7 additions & 20 deletions src/components/modules/blockEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,47 +211,34 @@ export default class BlockEvents extends Module {
* Copying selected blocks
* Before putting to the clipboard we sanitize all blocks and then copy to the clipboard
*
* @param event
* @param {ClipboardEvent} event
*/
public handleCommandC(event): void {
public handleCommandC(event: ClipboardEvent): void {
const { BlockSelection } = this.Editor;

if (!BlockSelection.anyBlockSelected) {
return;
}

/**
* Prevent default copy
* Remove "decline sound" on macOS
*/
event.preventDefault();

// Copy Selected Blocks
BlockSelection.copySelectedBlocks();
BlockSelection.copySelectedBlocks(event);
}

/**
* Copy and Delete selected Blocks
* @param event
* @param {ClipboardEvent} event
*/
public handleCommandX(event): void {
public handleCommandX(event: ClipboardEvent): void {
const { BlockSelection, BlockManager, Caret } = this.Editor;

if (!BlockSelection.anyBlockSelected) {
return;
}

/**
* Copy Blocks before removing
*
* Prevent default copy
* Remove "decline sound" on macOS
*/
event.preventDefault();

BlockSelection.copySelectedBlocks();
BlockSelection.copySelectedBlocks(event);

const selectionPositionIndex = BlockManager.removeSelectedBlocks();

Caret.setToBlock(BlockManager.insertInitialBlockAtIndex(selectionPositionIndex, true), Caret.positions.START);

/** Clear selection */
Expand Down
32 changes: 16 additions & 16 deletions src/components/modules/blockManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export default class BlockManager extends Module {
*/
public async prepare() {
const blocks = new Blocks(this.Editor.UI.nodes.redactor);
const { BlockEvents, Shortcuts } = this.Editor;
const { BlockEvents, Listeners } = this.Editor;

/**
* We need to use Proxy to overload set/get [] operator.
Expand All @@ -181,21 +181,19 @@ export default class BlockManager extends Module {
get: Blocks.get,
});

/** Copy shortcut */
Shortcuts.add({
name: 'CMD+C',
handler: (event) => {
BlockEvents.handleCommandC(event);
},
});
/** Copy event */
Listeners.on(
document,
'copy',
(e: ClipboardEvent) => BlockEvents.handleCommandC(e),
);

/** Copy and cut */
Shortcuts.add({
name: 'CMD+X',
handler: (event) => {
BlockEvents.handleCommandX(event);
},
});
Listeners.on(
document,
'cut',
(e: ClipboardEvent) => BlockEvents.handleCommandX(e),
);
}

/**
Expand Down Expand Up @@ -433,15 +431,17 @@ export default class BlockManager extends Module {
* Replace current working block
*
* @param {String} toolName — plugin name
* @param {Object} data — plugin data
* @param {BlockToolData} data — plugin data
* @param {ToolConfig} settings — plugin config
*
* @return {Block}
*/
public replace(
toolName: string = this.config.initialBlock,
data: BlockToolData = {},
settings: ToolConfig = {},
): Block {
const block = this.composeBlock(toolName, data);
const block = this.composeBlock(toolName, data, settings);

this._blocks.insert(this.currentBlockIndex, block, true);

Expand Down
22 changes: 19 additions & 3 deletions src/components/modules/blockSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,17 @@ export default class BlockSelection extends Module {

/**
* Reduce each Block and copy its content
neSpecc marked this conversation as resolved.
Show resolved Hide resolved
*
* @param {ClipboardEvent} e - copy/cut event
*
* @return Promise<void>
*/
public copySelectedBlocks(): void {
public async copySelectedBlocks(e: ClipboardEvent): Promise<void> {
/**
* Prevent default copy
*/
e.preventDefault();

const fakeClipboard = $.make('div');

this.selectedBlocks.forEach((block) => {
Expand All @@ -220,9 +229,16 @@ export default class BlockSelection extends Module {

fragment.innerHTML = cleanHTML;
fakeClipboard.appendChild(fragment);
});
});

const savedData = await Promise.all(this.selectedBlocks.map((block) => block.save()));

const textPlain = Array.from(fakeClipboard.childNodes).map((node) => node.textContent).join('\n\n');
const textHTML = fakeClipboard.innerHTML;

_.copyTextToClipboard(fakeClipboard.innerHTML);
e.clipboardData.setData('text/plain', textPlain);
e.clipboardData.setData('text/html', textHTML);
e.clipboardData.setData(this.Editor.Paste.MIME_TYPE, JSON.stringify(savedData));
}

/**
Expand Down
56 changes: 51 additions & 5 deletions src/components/modules/paste.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as _ from '../utils';
import {
BlockTool,
BlockToolConstructable,
BlockToolData,
PasteConfig,
PasteEvent,
PasteEventDetail,
Expand Down Expand Up @@ -102,6 +103,9 @@ export default class Paste extends Module {
/** If string`s length is greater than this number we don't check paste patterns */
public static readonly PATTERN_PROCESSING_MAX_LENGTH = 450;

/** Custom EditorJS mime-type to handle in-editor copy/paste actions */
public readonly MIME_TYPE = 'application/x-editor-js';

/**
* Tags` substitutions parameters
*/
Expand Down Expand Up @@ -156,9 +160,21 @@ export default class Paste extends Module {
return;
}

const editorJSData = dataTransfer.getData(this.MIME_TYPE);
const plainData = dataTransfer.getData('text/plain');
let htmlData = dataTransfer.getData('text/html');

/**
* If EditorJS json is passed, insert it
*/
if (editorJSData) {
try {
this.insertEditorJSData(JSON.parse(editorJSData));

return;
} catch (e) {} // Do nothing and continue execution as usual if error appears
}

/**
* If text was drag'n'dropped, wrap content with P tag to insert it as the new Block
*/
Expand Down Expand Up @@ -211,9 +227,9 @@ export default class Paste extends Module {
const isCurrentBlockInitial = BlockManager.currentBlock && Tools.isInitial(BlockManager.currentBlock.tool);
const needToReplaceCurrentBlock = isCurrentBlockInitial && BlockManager.currentBlock.isEmpty;

await Promise.all(dataToInsert.map(
async (content, i) => await this.insertBlock(content, i === 0 && needToReplaceCurrentBlock),
));
dataToInsert.map(
async (content, i) => this.insertBlock(content, i === 0 && needToReplaceCurrentBlock),
);

if (BlockManager.currentBlock) {
Caret.setToBlock(BlockManager.currentBlock, Caret.positions.END);
Expand Down Expand Up @@ -664,12 +680,13 @@ export default class Paste extends Module {
}

/**
* Insert pasted Block content to Editor
*
* @param {PasteData} data
* @param {Boolean} canReplaceCurrentBlock - if true and is current Block is empty, will replace current Block
* @returns {Promise<void>}
* @returns {void}
*/
private async insertBlock(data: PasteData, canReplaceCurrentBlock: boolean = false): Promise<void> {
private insertBlock(data: PasteData, canReplaceCurrentBlock: boolean = false): void {
const {BlockManager, Caret} = this.Editor;
const {currentBlock} = BlockManager;
let block: Block;
Expand All @@ -685,6 +702,35 @@ export default class Paste extends Module {
Caret.setToBlock(block, Caret.positions.END);
}

/**
* Insert data passed as application/x-editor-js JSON
*
* @param {object} blocks — Blocks' data to insert
*
* @return {void}
*/
private insertEditorJSData(blocks: Array<{tool: string, data: BlockToolData}>): void {
const { BlockManager, Tools } = this.Editor;

blocks.forEach(({ tool, data }, i) => {
const settings = this.Editor.Tools.getToolSettings(tool);

let needToReplaceCurrentBlock = false;

if (i === 0) {
const isCurrentBlockInitial = BlockManager.currentBlock && Tools.isInitial(BlockManager.currentBlock.tool);

needToReplaceCurrentBlock = isCurrentBlockInitial && BlockManager.currentBlock.isEmpty;
}

if (needToReplaceCurrentBlock) {
BlockManager.replace(tool, data, settings);
} else {
BlockManager.insert(tool, data, settings);
}
});
}

/**
* Recursively divide HTML string to two types of nodes:
* 1. Block element
Expand Down