Files
Trilium/packages/ckeditor5/src/plugins/move_block_updown.ts

114 lines
3.0 KiB
TypeScript
Raw Normal View History

/**
* https://github.com/TriliumNext/Notes/issues/1002
*/
import { Command, DocumentSelection, Element, Node, Plugin } from 'ckeditor5';
export default class MoveBlockUpDownPlugin extends Plugin {
init() {
const editor = this.editor;
editor.config.define('moveBlockUp', {
keystroke: ['ctrl+arrowup', 'alt+arrowup'],
});
editor.config.define('moveBlockDown', {
keystroke: ['ctrl+arrowdown', 'alt+arrowdown'],
});
editor.commands.add('moveBlockUp', new MoveBlockUpCommand(editor));
editor.commands.add('moveBlockDown', new MoveBlockDownCommand(editor));
for (const keystroke of editor.config.get('moveBlockUp.keystroke') ?? []) {
editor.keystrokes.set(keystroke, 'moveBlockUp');
}
for (const keystroke of editor.config.get('moveBlockDown.keystroke') ?? []) {
editor.keystrokes.set(keystroke, 'moveBlockDown');
}
}
}
abstract class MoveBlockUpDownCommand extends Command {
abstract getSibling(selectedBlock: Element): Node | null;
abstract get offset(): "before" | "after";
override execute() {
const model = this.editor.model;
const selection = model.document.selection;
const selectedBlocks = this.getSelectedBlocks(selection);
const isEnabled = selectedBlocks.length > 0
&& selectedBlocks.every(block => !!this.getSibling(block));
if (!isEnabled) {
return;
}
const movingBlocks = this.offset === 'before'
? selectedBlocks
: [...selectedBlocks].reverse();
// Store selection offsets
const offsets = [
model.document.selection.getFirstPosition()?.offset,
model.document.selection.getLastPosition()?.offset
];
model.change((writer) => {
// Move blocks
for (const block of movingBlocks) {
const sibling = this.getSibling(block);
if (sibling) {
const range = model.createRangeOn(block);
writer.move(range, sibling, this.offset);
}
}
// Restore selection to all items if many have been moved
const range = writer.createRange(
writer.createPositionAt(selectedBlocks[0], offsets[0]),
writer.createPositionAt(
selectedBlocks[selectedBlocks.length - 1], offsets[1]));
writer.setSelection(range);
this.scrollToSelection();
});
}
getSelectedBlocks(selection: DocumentSelection) {
return [...selection.getSelectedBlocks()];
}
scrollToSelection() {
// Ensure scroll happens in sync with DOM updates
requestAnimationFrame(() => {
this.editor.editing.view.scrollToTheSelection();
});
};
}
class MoveBlockUpCommand extends MoveBlockUpDownCommand {
getSibling(selectedBlock: Element) {
return selectedBlock.previousSibling;
}
get offset() {
return "before" as const;
}
}
class MoveBlockDownCommand extends MoveBlockUpDownCommand {
/** @override */
getSibling(selectedBlock: Element) {
return selectedBlock.nextSibling;
}
/** @override */
get offset() {
return "after" as const;
}
}