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

Custom #2823

Draft
wants to merge 5 commits into
base: next
Choose a base branch
from
Draft

Custom #2823

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
12 changes: 8 additions & 4 deletions src/components/block/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Block from './index';
import { BlockToolData, ToolConfig } from '../../../types/tools';
import { SavedData } from '../../../types/data-formats';
import { BlockAPI as BlockAPIInterface } from '../../../types/api';
import {BlockToolData, ToolConfig} from '../../../types/tools';
import {SavedData} from '../../../types/data-formats';
import {BlockAPI as BlockAPIInterface} from '../../../types/api';

/**
* Constructs new BlockAPI object
Expand Down Expand Up @@ -100,10 +100,14 @@ function BlockAPI(
*
* @returns {Promise<void|SavedData>}
*/
save(): Promise<void|SavedData> {
save(): Promise<void | SavedData> {
return block.save();
},

markdown(): Promise<string> {
return block.markdown();
},

/**
* Validate Block data
*
Expand Down
7 changes: 7 additions & 0 deletions src/components/block/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,13 @@ export default class Block extends EventsDispatcher<BlockEvents> {
return isValid;
}

public markdown(): Promise<string> {
if (this.toolInstance.markdown instanceof Function) {
return this.toolInstance.markdown();
}
return null;
}

/**
* Returns data to render in tunes menu.
* Splits block tunes settings into 2 groups: popover items and custom html.
Expand Down
74 changes: 52 additions & 22 deletions src/components/modules/blockEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as _ from '../utils';
import SelectionUtils from '../selection';
import Flipper from '../flipper';
import type Block from '../block';
import { areBlocksMergeable } from '../utils/blocks';
import {areBlocksMergeable} from '../utils/blocks';

/**
*
Expand Down Expand Up @@ -38,7 +38,9 @@ export default class BlockEvents extends Module {
case _.keyCodes.ENTER:
this.enter(event);
break;

case _.keyCodes.TAB:
this.enter(event);
break;
case _.keyCodes.DOWN:
case _.keyCodes.RIGHT:
this.arrowRightAndDown(event);
Expand All @@ -49,7 +51,7 @@ export default class BlockEvents extends Module {
this.arrowLeftAndUp(event);
break;

case _.keyCodes.TAB:
case (event.ctrlKey || event.metaKey) && _.keyCodes.SLASH:
this.tabPressed(event);
break;
}
Expand Down Expand Up @@ -124,7 +126,7 @@ export default class BlockEvents extends Module {
*/
this.Editor.BlockSelection.clearSelection(event);

const { BlockManager, InlineToolbar, ConversionToolbar } = this.Editor;
const {BlockManager, InlineToolbar, ConversionToolbar} = this.Editor;
const currentBlock = BlockManager.currentBlock;

if (!currentBlock) {
Expand Down Expand Up @@ -176,7 +178,7 @@ export default class BlockEvents extends Module {
* @param {ClipboardEvent} event - clipboard event
*/
public handleCommandC(event: ClipboardEvent): void {
const { BlockSelection } = this.Editor;
const {BlockSelection} = this.Editor;

if (!BlockSelection.anyBlockSelected) {
return;
Expand All @@ -192,7 +194,7 @@ export default class BlockEvents extends Module {
* @param {ClipboardEvent} event - clipboard event
*/
public handleCommandX(event: ClipboardEvent): void {
const { BlockSelection, BlockManager, Caret } = this.Editor;
const {BlockSelection, BlockManager, Caret} = this.Editor;

if (!BlockSelection.anyBlockSelected) {
return;
Expand All @@ -219,7 +221,7 @@ export default class BlockEvents extends Module {
* @param {KeyboardEvent} event - keydown
*/
private enter(event: KeyboardEvent): void {
const { BlockManager, UI } = this.Editor;
const {BlockManager, UI} = this.Editor;
const currentBlock = BlockManager.currentBlock;

/**
Expand All @@ -245,18 +247,28 @@ export default class BlockEvents extends Module {
return;
}

/**
* 处理普通文本首行tab
* @author asan
*/
if (event.keyCode === _.keyCodes.TAB && currentBlock.name === 'paragraph') {
event.preventDefault();
return;
}

let newCurrent = this.Editor.BlockManager.currentBlock;


/**
* If enter has been pressed at the start of the text, just insert paragraph Block above
*/
if (this.Editor.Caret.isAtStart && !this.Editor.BlockManager.currentBlock.hasMedia) {
this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex);

/**
* If caret is at very end of the block, just append the new block without splitting
* to prevent unnecessary dom mutation observing
*/
/**
* If caret is at very end of the block, just append the new block without splitting
* to prevent unnecessary dom mutation observing
*/
} else if (this.Editor.Caret.isAtEnd) {
newCurrent = this.Editor.BlockManager.insertDefaultBlockAtIndex(this.Editor.BlockManager.currentBlockIndex + 1);
} else {
Expand All @@ -283,8 +295,16 @@ export default class BlockEvents extends Module {
* @param {KeyboardEvent} event - keydown
*/
private backspace(event: KeyboardEvent): void {
const { BlockManager, Caret } = this.Editor;
const { currentBlock, previousBlock } = BlockManager;
const {BlockManager, Caret} = this.Editor;
const {currentBlock, previousBlock} = BlockManager;


/**
* 文本在非首行首列时,不处理backspace事件
*/
if (currentBlock.name === 'paragraph' && !Caret.isAtStartForText) {
return;
}

/**
* If some fragment is selected, leave native behaviour
Expand Down Expand Up @@ -366,9 +386,12 @@ export default class BlockEvents extends Module {
* @param {KeyboardEvent} event - keydown
*/
private delete(event: KeyboardEvent): void {
const { BlockManager, Caret } = this.Editor;
const { currentBlock, nextBlock } = BlockManager;
const {BlockManager, Caret} = this.Editor;
const {currentBlock, nextBlock} = BlockManager;

if (currentBlock.name === 'paragraph' && !Caret.isAtStartForText) {
return;
}
/**
* If some fragment is selected, leave native behaviour
*/
Expand Down Expand Up @@ -447,7 +470,7 @@ export default class BlockEvents extends Module {
* @param blockToMerge - what Block we want to merge
*/
private mergeBlocks(targetBlock: Block, blockToMerge: Block): void {
const { BlockManager, Caret, Toolbar } = this.Editor;
const {BlockManager, Caret, Toolbar} = this.Editor;

Caret.createShadow(targetBlock.pluginsContent);

Expand Down Expand Up @@ -511,7 +534,7 @@ export default class BlockEvents extends Module {
if (this.Editor.BlockManager.currentBlock) {
this.Editor.BlockManager.currentBlock.updateCurrentInput();
}
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
}, 20)();
}

Expand All @@ -527,6 +550,13 @@ export default class BlockEvents extends Module {
* @param {KeyboardEvent} event - keyboard event
*/
private arrowLeftAndUp(event: KeyboardEvent): void {
const {BlockManager, Caret} = this.Editor;
const {currentBlock} = BlockManager;

if (currentBlock.name === 'paragraph' && !Caret.isAtStartForText) {
return;
}

/**
* Arrows might be handled on toolbars by flipper
* Check for Flipper.usedKeys to allow navigate by UP and disallow by LEFT
Expand Down Expand Up @@ -570,7 +600,7 @@ export default class BlockEvents extends Module {
if (this.Editor.BlockManager.currentBlock) {
this.Editor.BlockManager.currentBlock.updateCurrentInput();
}
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
}, 20)();
}

Expand All @@ -587,10 +617,10 @@ export default class BlockEvents extends Module {
*/
private needToolbarClosing(event: KeyboardEvent): boolean {
const toolboxItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.Toolbar.toolbox.opened),
blockSettingsItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.BlockSettings.opened),
inlineToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.InlineToolbar.opened),
conversionToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.ConversionToolbar.opened),
flippingToolbarItems = event.keyCode === _.keyCodes.TAB;
blockSettingsItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.BlockSettings.opened),
inlineToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.InlineToolbar.opened),
conversionToolbarItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.ConversionToolbar.opened),
flippingToolbarItems = event.keyCode === _.keyCodes.TAB;

/**
* Do not close Toolbar in cases:
Expand Down
90 changes: 90 additions & 0 deletions src/components/modules/caret.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,96 @@ export default class Caret extends Module {
return firstNode === null || (focusNode === firstNode && focusOffset <= firstLetterPosition);
}

public get isAtStartForText(): boolean {
const selection = Selection.get();
if (selection['focusOffset'] !== 0) {
return false;
}
const firstNode = $.getDeepestNode(this.Editor.BlockManager.currentBlock.currentInput);
let focusNode = selection.focusNode;

/** In case lastNode is native input */
if ($.isNativeInput(firstNode)) {
return (firstNode as HTMLInputElement).selectionEnd === 0;
}

/** Case when selection have been cleared programmatically, for example after CBS */
if (!selection.anchorNode) {
return false;
}

/**
* Workaround case when caret in the text like " |Hello!"
* selection.anchorOffset is 1, but real caret visible position is 0
*
* @type {number}
*/

let firstLetterPosition = focusNode.textContent.search(/\S/);

if (firstLetterPosition === -1) { // empty text
firstLetterPosition = 0;
}

/**
* If caret was set by external code, it might be set to text node wrapper.
* <div>|hello</div> <---- Selection references to <div> instead of text node
*
* In this case, anchor node has ELEMENT_NODE node type.
* Anchor offset shows amount of children between start of the element and caret position.
*
* So we use child with focusOffset index as new anchorNode.
*/
let focusOffset = selection.focusOffset;

if (focusNode.nodeType !== Node.TEXT_NODE && focusNode.childNodes.length) {
if (focusNode.childNodes[focusOffset]) {
focusNode = focusNode.childNodes[focusOffset];
focusOffset = 0;
} else {
focusNode = focusNode.childNodes[focusOffset - 1];
focusOffset = focusNode.textContent.length;
}
}

/**
* In case of
* <div contenteditable>
* <p><b></b></p> <-- first (and deepest) node is <b></b>
* |adaddad <-- focus node
* </div>
*/
if ($.isLineBreakTag(firstNode as HTMLElement) || $.isEmpty(firstNode)) {
const leftSiblings = this.getHigherLevelSiblings(focusNode as HTMLElement, 'left');
const nothingAtLeft = leftSiblings.every((node) => {
/**
* Workaround case when block starts with several <br>'s (created by SHIFT+ENTER)
*
* @see https://github.com/codex-team/editor.js/issues/726
* We need to allow to delete such line breaks, so in this case caret IS NOT AT START
*/
const regularLineBreak = $.isLineBreakTag(node);
/**
* Workaround SHIFT+ENTER in Safari, that creates <div><br></div> instead of <br>
*/
const lineBreakInSafari = node.children.length === 1 && $.isLineBreakTag(node.children[0] as HTMLElement);
const isLineBreak = regularLineBreak || lineBreakInSafari;

return $.isEmpty(node) && !isLineBreak;
});

if (nothingAtLeft && focusOffset === firstLetterPosition) {
return true;
}
}

/**
* We use <= comparison for case:
* "| Hello" <--- selection.anchorOffset is 0, but firstLetterPosition is 1
*/
return firstNode === null || (focusNode === firstNode && focusOffset <= firstLetterPosition);
}

/**
* Get's deepest last node and checks if offset is last node text length
*
Expand Down
Loading