Handle paste to the same block (#578)

This commit is contained in:
George Berezhnoy 2018-12-27 16:01:00 +03:00 committed by GitHub
parent 9d0d638d42
commit 7ca07c7999
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 97 additions and 14 deletions

File diff suppressed because one or more lines are too long

View file

@ -1,6 +1,6 @@
{
"name": "codex.editor",
"version": "2.7.20",
"version": "2.7.21",
"description": "CodeX Editor. Native JS, based on API and Open Source",
"main": "dist/codex-editor.js",
"types": "./types/index.d.ts",

View file

@ -460,4 +460,27 @@ export default class Dom {
'video',
];
}
/**
* Check if passed content includes only inline elements
*
* @param {string|HTMLElement} data - element or html string
* @return {boolean}
*/
public static containsOnlyInlineElements(data: string | HTMLElement): boolean {
const wrapper = document.createElement('template');
if (typeof data === 'string') {
wrapper.innerHTML = data;
} else {
wrapper.appendChild(data);
}
const check = (element: HTMLElement) => {
return !Dom.blockElements.includes(element.tagName.toLowerCase())
&& Array.from(element.children).every(check);
};
return check(wrapper);
}
}

View file

@ -433,6 +433,35 @@ export default class Caret extends Module {
}, 50);
}
/**
* Inserts passed content at caret position
*
* @param {string} content - content to insert
*/
public insertContentAtCaretPosition(content: string): void {
const fragment = document.createDocumentFragment();
const wrapper = document.createElement('div');
const selection = Selection.get();
const range = Selection.range;
wrapper.innerHTML = content;
Array.from(wrapper.childNodes).forEach((child: Node) => fragment.appendChild(child));
const lastChild = fragment.lastChild;
range.deleteContents();
range.insertNode(fragment);
/** Cross-browser caret insertion */
const newRange = document.createRange();
newRange.setStart(lastChild, lastChild.textContent.length);
selection.removeAllRanges();
selection.addRange(newRange);
}
/**
* Get all first-level (first child of [contenteditabel]) siblings from passed node
* Then you can check it for emptiness

View file

@ -1,10 +1,15 @@
import CaretClass from './caret';
import SelectionUtils from '../selection';
import Module from '../__module';
import $ from '../dom';
import _ from '../utils';
import {BlockTool, BlockToolConstructable, PasteConfig, PasteEvent, PasteEventDetail} from '../../../types';
import {
BlockTool,
BlockToolConstructable,
PasteConfig,
PasteEvent,
PasteEventDetail,
} from '../../../types';
import Block from '../block';
/**
* Tag substitute object.
@ -432,8 +437,12 @@ export default class Paste extends Module {
return;
}
if (dataToInsert.length === 1 && !dataToInsert[0].isBlock) {
this.processSingleBlock(dataToInsert.pop());
if (dataToInsert.length === 1) {
if (!dataToInsert[0].isBlock) {
this.processInlinePaste(dataToInsert.pop());
} else {
this.processSingleBlock(dataToInsert.pop());
}
return;
}
@ -536,6 +545,26 @@ export default class Paste extends Module {
});
}
/**
* Process paste of single Block tool content
*
* @param {PasteData} dataToInsert
*/
private async processSingleBlock(dataToInsert: PasteData): Promise<void> {
const {Caret, BlockManager, Tools} = this.Editor;
const {currentBlock} = BlockManager;
/**
* If pasted tool isn`t equal current Block or if pasted content contains block elements, insert it as new Block
*/
if (dataToInsert.tool !== currentBlock.name || !$.containsOnlyInlineElements(dataToInsert.content.innerHTML)) {
this.insertBlock(dataToInsert, Tools.isInitial(currentBlock.tool) && currentBlock.isEmpty);
return;
}
Caret.insertContentAtCaretPosition(dataToInsert.content.innerHTML);
}
/**
* Process paste to single Block:
* 1. Find patterns` matches
@ -544,7 +573,7 @@ export default class Paste extends Module {
*
* @param {PasteData} dataToInsert
*/
private async processSingleBlock(dataToInsert: PasteData): Promise<void> {
private async processInlinePaste(dataToInsert: PasteData): Promise<void> {
const initialTool = this.config.initialBlock,
{BlockManager, Caret, Sanitizer, Tools} = this.Editor,
{content, tool} = dataToInsert;
@ -613,15 +642,17 @@ export default class Paste extends Module {
private async insertBlock(data: PasteData, canReplaceCurrentBlock: boolean = false): Promise<void> {
const {BlockManager, Caret} = this.Editor;
const {currentBlock} = BlockManager;
let block: Block;
if (canReplaceCurrentBlock && currentBlock && currentBlock.isEmpty) {
BlockManager.paste(data.tool, data.event, true);
block = BlockManager.paste(data.tool, data.event, true);
Caret.setToBlock(block, CaretClass.positions.END);
return;
}
const block = BlockManager.paste(data.tool, data.event);
block = BlockManager.paste(data.tool, data.event);
Caret.setToBlock(block);
Caret.setToBlock(block, CaretClass.positions.END);
}
/**
@ -678,7 +709,7 @@ export default class Paste extends Module {
);
/** Append inline elements to previous fragment */
if (!isBlockElement && !isSubstitutable) {
if (!isBlockElement && !isSubstitutable && !containsAnotherToolTags) {
destNode.appendChild(element);
return [...nodes, destNode];
}