UNPKG

@ckeditor/ckeditor5-enter

Version:

Enter feature for CKEditor 5.

97 lines (96 loc) 3.77 kB
/** * @license Copyright (c) 2003-2026, CKSource Holding sp. z o.o. All rights reserved. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options */ /** * @module enter/entercommand */ import { Command } from '@ckeditor/ckeditor5-core'; import { getCopyOnEnterAttributes } from './utils.js'; /** * Enter command used by the {@link module:enter/enter~Enter Enter feature} to handle the <kbd>Enter</kbd> keystroke. */ export class EnterCommand extends Command { /** * @inheritDoc */ execute() { this.editor.model.change(writer => { this.enterBlock(writer); this.fire('afterExecute', { writer }); }); } /** * Splits a block where the document selection is placed, in the way how the <kbd>Enter</kbd> key is expected to work: * * ``` * <p>Foo[]bar</p> -> <p>Foo</p><p>[]bar</p> * <p>Foobar[]</p> -> <p>Foobar</p><p>[]</p> * <p>Fo[ob]ar</p> -> <p>Fo</p><p>[]ar</p> * ``` * * In some cases, the split will not happen: * * ``` * // The selection parent is a limit element: * <figcaption>A[bc]d</figcaption> -> <figcaption>A[]d</figcaption> * * // The selection spans over multiple elements: * <h>x[x</h><p>y]y<p> -> <h>x</h><p>[]y</p> * ``` * * @param writer Writer to use when performing the enter action. * @returns Boolean indicating if the block was split. */ enterBlock(writer) { const model = this.editor.model; const selection = model.document.selection; const schema = model.schema; const isSelectionEmpty = selection.isCollapsed; const range = selection.getFirstRange(); const startElement = range.start.parent; const endElement = range.end.parent; // Don't touch the roots and other limit elements. if (schema.isLimit(startElement) || schema.isLimit(endElement)) { // Delete the selected content but only if inside a single limit element. // Abort, when crossing limit elements boundary (e.g. <limit1>x[x</limit1>donttouchme<limit2>y]y</limit2>). // This is an edge case and it's hard to tell what should actually happen because such a selection // is not entirely valid. if (!isSelectionEmpty && startElement == endElement) { model.deleteContent(selection); } return false; } if (isSelectionEmpty) { const attributesToCopy = getCopyOnEnterAttributes(writer.model.schema, selection.getAttributes()); splitBlock(writer, range.start); writer.setSelectionAttribute(attributesToCopy); return true; } else { const leaveUnmerged = !(range.start.isAtStart && range.end.isAtEnd); const isContainedWithinOneElement = (startElement == endElement); model.deleteContent(selection, { leaveUnmerged }); if (leaveUnmerged) { // Partially selected elements. // // <h>x[xx]x</h> -> <h>x^x</h> -> <h>x</h><h>^x</h> if (isContainedWithinOneElement) { splitBlock(writer, selection.focus); return true; } // Selection over multiple elements. // // <h>x[x</h><p>y]y<p> -> <h>x^</h><p>y</p> -> <h>x</h><p>^y</p> else { writer.setSelection(endElement, 0); } } } return false; } } function splitBlock(writer, splitPos) { writer.split(splitPos); writer.setSelection(splitPos.parent.nextSibling, 0); }