mirror of
https://github.com/codex-team/editor.js
synced 2024-06-29 02:40:23 +02:00
Implement navigateNext()
This commit is contained in:
parent
5f64c0c566
commit
75c3010443
|
@ -16,6 +16,7 @@
|
|||
},
|
||||
"globals": {
|
||||
"Node": true,
|
||||
"NodeFilter": true,
|
||||
"Range": true,
|
||||
"HTMLElement": true,
|
||||
"HTMLDivElement": true,
|
||||
|
|
1
dist/editor.js.map
vendored
Normal file
1
dist/editor.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -403,16 +403,17 @@ export default class BlockEvents extends Module {
|
|||
this.Editor.BlockManager.clearFocused();
|
||||
this.Editor.Toolbar.close();
|
||||
|
||||
const isDownPressed = event.keyCode === _.keyCodes.DOWN;
|
||||
const shouldEnableCBS = this.Editor.Caret.isAtEnd || this.Editor.BlockSelection.anyBlockSelected;
|
||||
|
||||
if (event.shiftKey && event.keyCode === _.keyCodes.DOWN && shouldEnableCBS) {
|
||||
if (event.shiftKey && isDownPressed && shouldEnableCBS) {
|
||||
this.Editor.CrossBlockSelection.toggleBlockSelectedState();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const navigateNext = event.keyCode === _.keyCodes.DOWN || (event.keyCode === _.keyCodes.RIGHT && !this.isRtl);
|
||||
const isNavigated = navigateNext ? this.Editor.Caret.navigateNext() : this.Editor.Caret.navigatePrevious();
|
||||
const navigateNext = isDownPressed || (event.keyCode === _.keyCodes.RIGHT && !this.isRtl);
|
||||
const isNavigated = navigateNext ? this.Editor.Caret.navigateNext(isDownPressed) : this.Editor.Caret.navigatePrevious();
|
||||
|
||||
if (isNavigated) {
|
||||
/**
|
||||
|
@ -470,7 +471,7 @@ export default class BlockEvents extends Module {
|
|||
}
|
||||
|
||||
const navigatePrevious = event.keyCode === _.keyCodes.UP || (event.keyCode === _.keyCodes.LEFT && !this.isRtl);
|
||||
const isNavigated = navigatePrevious ? this.Editor.Caret.navigatePrevious() : this.Editor.Caret.navigateNext();
|
||||
const isNavigated = navigatePrevious ? this.Editor.Caret.navigatePrevious() : this.Editor.Caret.navigateNext(false);
|
||||
|
||||
if (isNavigated) {
|
||||
/**
|
||||
|
|
|
@ -388,44 +388,25 @@ export default class Caret extends Module {
|
|||
* Before moving caret, we should check if caret position is at the end of Plugins node
|
||||
* Using {@link Dom#getDeepestNode} to get a last node and match with current selection
|
||||
*
|
||||
* @param {boolean} isDownPressed - Is Down key pressed
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
public navigateNext(): boolean {
|
||||
const { BlockManager, Tools } = this.Editor;
|
||||
const { currentBlock, nextContentfulBlock } = BlockManager;
|
||||
const { nextInput } = currentBlock;
|
||||
const isAtEnd = this.isAtEnd;
|
||||
public navigateNext(isDownPressed: boolean): boolean {
|
||||
const shouldNavigateToNext = this.isAtEnd || (isDownPressed && !this.isNextLineExisted());
|
||||
const next = shouldNavigateToNext && this.detectNext();
|
||||
|
||||
let nextBlock = nextContentfulBlock;
|
||||
if (next) {
|
||||
const offset = isDownPressed ? next.downOffset : 0;
|
||||
const position = offset === undefined ? this.positions.END : this.positions.DEFAULT;
|
||||
|
||||
if (!nextBlock && !nextInput) {
|
||||
/**
|
||||
* This code allows to exit from the last non-initial tool:
|
||||
* https://github.com/codex-team/editor.js/issues/1103
|
||||
*/
|
||||
|
||||
/**
|
||||
* 1. If there is a last block and it is default, do nothing
|
||||
* 2. If there is a last block and it is non-default --> and caret not at the end <--, do nothing
|
||||
* (https://github.com/codex-team/editor.js/issues/1414)
|
||||
*/
|
||||
if (Tools.isDefault(currentBlock.tool) || !isAtEnd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is no nextBlock, but currentBlock is not default,
|
||||
* insert new default block at the end and navigate to it
|
||||
*/
|
||||
nextBlock = BlockManager.insertAtEnd();
|
||||
}
|
||||
|
||||
if (isAtEnd) {
|
||||
/** If next Tool`s input exists, focus on it. Otherwise set caret to the next Block */
|
||||
if (!nextInput) {
|
||||
this.setToBlock(nextBlock, this.positions.START);
|
||||
if (next.nextInput) {
|
||||
// TODO: It may be have a bug with ignore inline tags
|
||||
this.setToInput(next.nextInput, position, offset);
|
||||
} else {
|
||||
this.setToInput(nextInput, this.positions.START);
|
||||
// TODO: It may be have a bug with ignore inline tags
|
||||
this.setToBlock(next.nextBlock, position, offset);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -548,6 +529,73 @@ export default class Caret extends Module {
|
|||
selection.addRange(newRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: JSDoc
|
||||
* TODO: specify return type
|
||||
*/
|
||||
private detectNext() {
|
||||
const { BlockManager, Tools } = this.Editor;
|
||||
|
||||
const nextInput = BlockManager.currentBlock.nextInput;
|
||||
let nextBlock = BlockManager.nextContentfulBlock;
|
||||
|
||||
if (!nextBlock && !nextInput) {
|
||||
/**
|
||||
* If there is a last block and it is default, do nothing
|
||||
* This code allows to exit from the last non-initial tool:
|
||||
* https://github.com/codex-team/editor.js/issues/1103
|
||||
*/
|
||||
if (Tools.isDefault(BlockManager.currentBlock.tool)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is no nextBlock, but currentBlock is not default,
|
||||
* insert new default block at the end and navigate to it
|
||||
*/
|
||||
nextBlock = BlockManager.insertAtEnd();
|
||||
}
|
||||
|
||||
const currentBoundingClientRect = Selection.get().getRangeAt(0)
|
||||
.getBoundingClientRect();
|
||||
const range = new Range();
|
||||
const treeWalker = document.createTreeWalker(nextInput ?? nextBlock.firstInput, NodeFilter.SHOW_TEXT);
|
||||
|
||||
let node = treeWalker.firstChild();
|
||||
let offset = 0;
|
||||
let prevX: number | undefined;
|
||||
|
||||
while (node) {
|
||||
if (!(node instanceof Text)) {
|
||||
throw new Error('Unexpected node type');
|
||||
}
|
||||
|
||||
for (let index = 0; index < node.length; index++) {
|
||||
range.setStart(node, index);
|
||||
|
||||
const boundingClientRect = range.getBoundingClientRect();
|
||||
|
||||
if (prevX !== undefined && Math.abs(currentBoundingClientRect.x - prevX) < Math.abs(currentBoundingClientRect.x - boundingClientRect.x)) {
|
||||
return {
|
||||
nextBlock,
|
||||
nextInput,
|
||||
downOffset: offset - 1,
|
||||
};
|
||||
}
|
||||
|
||||
offset++;
|
||||
prevX = boundingClientRect.x;
|
||||
}
|
||||
|
||||
node = treeWalker.nextNode();
|
||||
}
|
||||
|
||||
return {
|
||||
nextBlock,
|
||||
nextInput,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all first-level (first child of [contenteditabel]) siblings from passed node
|
||||
* Then you can check it for emptiness
|
||||
|
@ -591,4 +639,38 @@ export default class Caret extends Module {
|
|||
|
||||
return siblings;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: JSDoc
|
||||
*/
|
||||
private isNextLineExisted(): boolean {
|
||||
const { BlockManager } = this.Editor;
|
||||
|
||||
const currentBoundingClientRect = Selection.get().getRangeAt(0)
|
||||
.getBoundingClientRect();
|
||||
const range = new Range();
|
||||
const treeWalker = document.createTreeWalker(BlockManager.currentBlock.currentInput, NodeFilter.SHOW_TEXT);
|
||||
|
||||
let node = treeWalker.firstChild();
|
||||
|
||||
while (node) {
|
||||
if (!(node instanceof Text)) {
|
||||
throw new Error('Unexpected node type');
|
||||
}
|
||||
|
||||
for (let index = 0; index < node.length; index++) {
|
||||
range.setStart(node, index);
|
||||
|
||||
const boundingClientRect = range.getBoundingClientRect();
|
||||
|
||||
if (currentBoundingClientRect.y < boundingClientRect.y) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
node = treeWalker.nextNode();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue