Skip to content

Commit

Permalink
Merge pull request #12647 from ckeditor/ck/12609-ts-paragraph
Browse files Browse the repository at this point in the history
Other (paragraph): Rewrites ckeditor5-paragraph to TypeScript. Closes #12609.
  • Loading branch information
arkflpc authored Oct 14, 2022
2 parents 56ecd68 + 988a635 commit 9c52490
Show file tree
Hide file tree
Showing 14 changed files with 366 additions and 4 deletions.
2 changes: 2 additions & 0 deletions packages/ckeditor5-engine/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export { default as MarkerOperation } from './model/operation/markeroperation';
export { default as OperationFactory } from './model/operation/operationfactory';
export { transformSets } from './model/operation/transform';

export { default as Selection } from './model/selection';
export { default as DocumentSelection } from './model/documentselection';
export { default as Range } from './model/range';
export { default as LiveRange } from './model/liverange';
Expand All @@ -32,6 +33,7 @@ export { default as Position } from './model/position';
export { default as DocumentFragment } from './model/documentfragment';
export { default as History } from './model/history';
export { default as Text } from './model/text';
export { default as Schema } from './model/schema';

export { default as DomConverter } from './view/domconverter';
export { default as Renderer } from './view/renderer';
Expand Down
File renamed without changes.
16 changes: 12 additions & 4 deletions packages/ckeditor5-paragraph/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"ckeditor5-plugin",
"ckeditor5-dll"
],
"main": "src/index.js",
"main": "src/index.ts",
"dependencies": {
"@ckeditor/ckeditor5-core": "^35.2.1",
"@ckeditor/ckeditor5-ui": "^35.2.1",
Expand All @@ -25,7 +25,10 @@
"@ckeditor/ckeditor5-heading": "^35.2.1",
"@ckeditor/ckeditor5-link": "^35.2.1",
"@ckeditor/ckeditor5-typing": "^35.2.1",
"@ckeditor/ckeditor5-undo": "^35.2.1"
"@ckeditor/ckeditor5-undo": "^35.2.1",
"typescript": "^4.8.4",
"webpack": "^5.58.1",
"webpack-cli": "^4.9.0"
},
"engines": {
"node": ">=14.0.0",
Expand All @@ -42,9 +45,14 @@
},
"files": [
"lang",
"src",
"src/**/*.js",
"src/**/*.d.ts",
"theme",
"ckeditor5-metadata.json",
"CHANGELOG.md"
]
],
"scripts": {
"build": "tsc -p ./tsconfig.release.json",
"postversion": "npm run build"
}
}
11 changes: 11 additions & 0 deletions packages/ckeditor5-paragraph/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module paragraph
*/

export { default as Paragraph } from './paragraph';
export { default as ParagraphButtonUI } from './paragraphbuttonui';
73 changes: 73 additions & 0 deletions packages/ckeditor5-paragraph/src/insertparagraphcommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module paragraph/insertparagraphcommand
*/

import Command from '@ckeditor/ckeditor5-core/src/command';
import type { Element, Position } from '@ckeditor/ckeditor5-engine';

/**
* The insert paragraph command. It inserts a new paragraph at a specific
* {@link module:engine/model/position~Position document position}.
*
* // Insert a new paragraph before an element in the document.
* editor.execute( 'insertParagraph', {
* position: editor.model.createPositionBefore( element )
* } );
*
* If a paragraph is disallowed in the context of the specific position, the command
* will attempt to split position ancestors to find a place where it is possible
* to insert a paragraph.
*
* **Note**: This command moves the selection to the inserted paragraph.
*
* @extends module:core/command~Command
*/
export default class InsertParagraphCommand extends Command {
/**
* Executes the command.
*
* @param {Object} options Options for the executed command.
* @param {module:engine/model/position~Position} options.position The model position at which
* the new paragraph will be inserted.
* @param {Object} attributes Attributes keys and values to set on a inserted paragraph
* @fires execute
*/
public override execute( options: {
position: Position;
attributes: Record<string, unknown>;
} ): void {
const model = this.editor.model;
const attributes = options.attributes;

let position = options.position;

model.change( writer => {
const paragraph = writer.createElement( 'paragraph' );

if ( attributes ) {
model.schema.setAllowedAttributes( paragraph, attributes, writer );
}

if ( !model.schema.checkChild( position.parent as Element, paragraph ) ) {
const allowedParent = model.schema.findAllowedParent( position, paragraph );

// It could be there's no ancestor limit that would allow paragraph.
// In theory, "paragraph" could be disallowed even in the "$root".
if ( !allowedParent ) {
return;
}

position = writer.split( position, allowedParent ).position;
}

model.insertContent( paragraph, position );

writer.setSelection( paragraph, 'in' );
} );
}
}
118 changes: 118 additions & 0 deletions packages/ckeditor5-paragraph/src/paragraph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module paragraph/paragraph
*/

import ParagraphCommand from './paragraphcommand';
import InsertParagraphCommand from './insertparagraphcommand';

import Plugin from '@ckeditor/ckeditor5-core/src/plugin';

/**
* The paragraph feature for the editor.
*
* It introduces the `<paragraph>` element in the model which renders as a `<p>` element in the DOM and data.
*
* It also brings two editors commands:
*
* * The {@link module:paragraph/paragraphcommand~ParagraphCommand `'paragraph'`} command that converts all
* blocks in the model selection into paragraphs.
* * The {@link module:paragraph/insertparagraphcommand~InsertParagraphCommand `'insertParagraph'`} command
* that inserts a new paragraph at a specified location in the model.
*
* @extends module:core/plugin~Plugin
*/
export default class Paragraph extends Plugin {
/**
* @inheritDoc
*/
public static get pluginName(): string {
return 'Paragraph';
}

/**
* @inheritDoc
*/
public init(): void {
const editor = this.editor;
const model = editor.model;

editor.commands.add( 'paragraph', new ParagraphCommand( editor ) );
editor.commands.add( 'insertParagraph', new InsertParagraphCommand( editor ) );

// Schema.
model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );

editor.conversion.elementToElement( { model: 'paragraph', view: 'p' } );

// Conversion for paragraph-like elements which has not been converted by any plugin.
editor.conversion.for( 'upcast' ).elementToElement( {
model: ( viewElement, { writer } ) => {
if ( !Paragraph.paragraphLikeElements.has( viewElement.name ) ) {
return null;
}

// Do not auto-paragraph empty elements.
if ( viewElement.isEmpty ) {
return null;
}

return writer.createElement( 'paragraph' );
},
view: /.+/,
converterPriority: 'low'
} );
}

/**
* A list of element names which should be treated by the autoparagraphing algorithms as
* paragraph-like. This means that e.g. the following content:
*
* <h1>Foo</h1>
* <table>
* <tr>
* <td>X</td>
* <td>
* <ul>
* <li>Y</li>
* <li>Z</li>
* </ul>
* </td>
* </tr>
* </table>
*
* contains five paragraph-like elements: `<h1>`, two `<td>`s and two `<li>`s.
* Hence, if none of the features is going to convert those elements the above content will be automatically handled
* by the paragraph feature and converted to:
*
* <p>Foo</p>
* <p>X</p>
* <p>Y</p>
* <p>Z</p>
*
* Note: The `<td>` containing two `<li>` elements was ignored as the innermost paragraph-like elements
* have a priority upon conversion.
*
* @member {Set.<String>} module:paragraph/paragraph~Paragraph.paragraphLikeElements
*/
public static paragraphLikeElements = new Set( [
'blockquote',
'dd',
'div',
'dt',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'li',
'p',
'td',
'th'
] );
}
55 changes: 55 additions & 0 deletions packages/ckeditor5-paragraph/src/paragraphbuttonui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module paragraph/paragraphbuttonui
*/

import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import icon from '@ckeditor/ckeditor5-core/theme/icons/paragraph.svg';
import type ParagraphCommand from './paragraphcommand';

/**
* This plugin defines the `'paragraph'` button. It can be used together with
* {@link module:heading/headingbuttonsui~HeadingButtonsUI} to replace the standard heading dropdown.
*
* This plugin is not loaded automatically by the {@link module:paragraph/paragraph~Paragraph} plugin. It must
* be added manually.
*
* ClassicEditor
* .create( {
* plugins: [ ..., Heading, Paragraph, HeadingButtonsUI, ParagraphButtonUI ]
* toolbar: [ 'paragraph', 'heading1', 'heading2', 'heading3' ]
* } )
* .then( ... )
* .catch( ... );
*
* @extends module:core/plugin~Plugin
*/
export default class ParagraphButtonUI extends Plugin {
public init(): void {
const editor = this.editor;
const t = editor.t;

editor.ui.componentFactory.add( 'paragraph', locale => {
const view = new ButtonView( locale );
const command = editor.commands.get( 'paragraph' ) as ParagraphCommand;

view.label = t( 'Paragraph' );
view.icon = icon;
view.tooltip = true;
view.isToggleable = true;
view.bind( 'isEnabled' ).to( command );
view.bind( 'isOn' ).to( command, 'value' );

view.on( 'execute', () => {
editor.execute( 'paragraph' );
} );

return view;
} );
}
}
78 changes: 78 additions & 0 deletions packages/ckeditor5-paragraph/src/paragraphcommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/**
* @module paragraph/paragraphcommand
*/

import Command from '@ckeditor/ckeditor5-core/src/command';
import first from '@ckeditor/ckeditor5-utils/src/first';

import type { Schema, Selection, DocumentSelection, Element } from '@ckeditor/ckeditor5-engine';

/**
* The paragraph command.
*
* @extends module:core/command~Command
*/
export default class ParagraphCommand extends Command {
/**
* The value of the command. Indicates whether the selection start is placed in a paragraph.
*
* @readonly
* @observable
* @member {Boolean} #value
*/
declare public value: boolean;

/**
* @inheritDoc
*/
public override refresh(): void {
const model = this.editor.model;
const document = model.document;
const block = first( document.selection.getSelectedBlocks() );

this.value = !!block && block.is( 'element', 'paragraph' );
this.isEnabled = !!block && checkCanBecomeParagraph( block, model.schema );
}

/**
* Executes the command. All the blocks (see {@link module:engine/model/schema~Schema}) in the selection
* will be turned to paragraphs.
*
* @fires execute
* @param {Object} [options] Options for the executed command.
* @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} [options.selection]
* The selection that the command should be applied to.
* By default, if not provided, the command is applied to the {@link module:engine/model/document~Document#selection}.
*/
public override execute( options: {
selection?: Selection | DocumentSelection;
} = {} ): void {
const model = this.editor.model;
const document = model.document;

model.change( writer => {
const blocks = ( options.selection || document.selection ).getSelectedBlocks();

for ( const block of blocks ) {
if ( !block.is( 'element', 'paragraph' ) && checkCanBecomeParagraph( block, model.schema ) ) {
writer.rename( block, 'paragraph' );
}
}
} );
}
}

// Checks whether the given block can be replaced by a paragraph.
//
// @private
// @param {module:engine/model/element~Element} block A block to be tested.
// @param {module:engine/model/schema~Schema} schema The schema of the document.
// @returns {Boolean}
function checkCanBecomeParagraph( block: Element, schema: Schema ) {
return schema.checkChild( block.parent as Element, 'paragraph' ) && !schema.isObject( block );
}
Loading

0 comments on commit 9c52490

Please sign in to comment.