@ckeditor/ckeditor5-heading
Version:
Headings feature for CKEditor 5.
108 lines (107 loc) • 4.07 kB
JavaScript
/**
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/
/**
* @module heading/headingediting
*/
import { Plugin } from 'ckeditor5/src/core.js';
import { Paragraph } from 'ckeditor5/src/paragraph.js';
import { priorities } from 'ckeditor5/src/utils.js';
import HeadingCommand from './headingcommand.js';
const defaultModelElement = 'paragraph';
/**
* The headings engine feature. It handles switching between block formats – headings and paragraph.
* This class represents the engine part of the heading feature. See also {@link module:heading/heading~Heading}.
* It introduces `heading1`-`headingN` commands which allow to convert paragraphs into headings.
*/
export default class HeadingEditing extends Plugin {
/**
* @inheritDoc
*/
static get pluginName() {
return 'HeadingEditing';
}
/**
* @inheritDoc
*/
static get isOfficialPlugin() {
return true;
}
/**
* @inheritDoc
*/
constructor(editor) {
super(editor);
editor.config.define('heading', {
options: [
{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
{ model: 'heading1', view: 'h2', title: 'Heading 1', class: 'ck-heading_heading1' },
{ model: 'heading2', view: 'h3', title: 'Heading 2', class: 'ck-heading_heading2' },
{ model: 'heading3', view: 'h4', title: 'Heading 3', class: 'ck-heading_heading3' }
]
});
}
/**
* @inheritDoc
*/
static get requires() {
return [Paragraph];
}
/**
* @inheritDoc
*/
init() {
const editor = this.editor;
const options = editor.config.get('heading.options');
const modelElements = [];
for (const option of options) {
// Skip paragraph - it is defined in required Paragraph feature.
if (option.model === 'paragraph') {
continue;
}
// Schema.
editor.model.schema.register(option.model, {
inheritAllFrom: '$block'
});
editor.conversion.elementToElement(option);
modelElements.push(option.model);
}
this._addDefaultH1Conversion(editor);
// Register the heading command for this option.
editor.commands.add('heading', new HeadingCommand(editor, modelElements));
}
/**
* @inheritDoc
*/
afterInit() {
// If the enter command is added to the editor, alter its behavior.
// Enter at the end of a heading element should create a paragraph.
const editor = this.editor;
const enterCommand = editor.commands.get('enter');
const options = editor.config.get('heading.options');
if (enterCommand) {
this.listenTo(enterCommand, 'afterExecute', (evt, data) => {
const positionParent = editor.model.document.selection.getFirstPosition().parent;
const isHeading = options.some(option => positionParent.is('element', option.model));
if (isHeading && !positionParent.is('element', defaultModelElement) && positionParent.childCount === 0) {
data.writer.rename(positionParent, defaultModelElement);
}
});
}
}
/**
* Adds default conversion for `h1` -> `heading1` with a low priority.
*
* @param editor Editor instance on which to add the `h1` conversion.
*/
_addDefaultH1Conversion(editor) {
editor.conversion.for('upcast').elementToElement({
model: 'heading1',
view: 'h1',
// With a `low` priority, `paragraph` plugin autoparagraphing mechanism is executed. Make sure
// this listener is called before it. If not, `h1` will be transformed into a paragraph.
converterPriority: priorities.low + 1
});
}
}