Skip to content

Commit

Permalink
Merge pull request #13149 from ckeditor/ck/13026
Browse files Browse the repository at this point in the history
Other (alignment): Rewrite ckeditor5-page-break to TypeScript. Closes #13026.
  • Loading branch information
arkflpc authored Dec 29, 2022
2 parents 4dee6e4 + 69ed2b2 commit b88a562
Show file tree
Hide file tree
Showing 14 changed files with 355 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ packages/ckeditor5-enter/src/**/*.js
packages/ckeditor5-essentials/src/**/*.js
packages/ckeditor5-horizontal-line/src/**/*.js
packages/ckeditor5-list/src/**/*.js
packages/ckeditor5-page-break/src/**/*.js
packages/ckeditor5-paragraph/src/**/*.js
packages/ckeditor5-paste-from-office/src/**/*.js
packages/ckeditor5-select-all/src/**/*.js
Expand Down
File renamed without changes.
10 changes: 7 additions & 3 deletions packages/ckeditor5-page-break/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": {
"ckeditor5": "^35.4.0"
},
Expand All @@ -26,6 +26,7 @@
"@ckeditor/ckeditor5-theme-lark": "^35.4.0",
"@ckeditor/ckeditor5-ui": "^35.4.0",
"@ckeditor/ckeditor5-widget": "^35.4.0",
"typescript": "^4.8.4",
"webpack": "^5.58.1",
"webpack-cli": "^4.9.0"
},
Expand All @@ -44,13 +45,16 @@
},
"files": [
"lang",
"src",
"src/**/*.js",
"src/**/*.d.ts",
"theme",
"build",
"ckeditor5-metadata.json",
"CHANGELOG.md"
],
"scripts": {
"dll:build": "webpack"
"dll:build": "webpack",
"build": "tsc -p ./tsconfig.release.json",
"postversion": "npm run build"
}
}
12 changes: 12 additions & 0 deletions packages/ckeditor5-page-break/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* @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 page-break
*/

export { default as PageBreak } from './pagebreak';
export { default as PageBreakEditing } from './pagebreakediting';
export { default as PageBreakUI } from './pagebreakui';
43 changes: 43 additions & 0 deletions packages/ckeditor5-page-break/src/pagebreak.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* @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 page-break/pagebreak
*/

import { Plugin, type PluginDependencies } from 'ckeditor5/src/core';
import { Widget } from 'ckeditor5/src/widget';

import PageBreakEditing from './pagebreakediting';
import PageBreakUI from './pagebreakui';

/**
* The page break feature.
*
* It provides the possibility to insert a page break into the rich-text editor.
*
* For a detailed overview, check the {@glink features/page-break Page break feature} documentation.
*/
export default class PageBreak extends Plugin {
/**
* @inheritDoc
*/
public static get requires(): PluginDependencies {
return [ PageBreakEditing, PageBreakUI, Widget ];
}

/**
* @inheritDoc
*/
public static get pluginName(): 'PageBreak' {
return 'PageBreak';
}
}

declare module '@ckeditor/ckeditor5-core' {
interface PluginsMap {
[ PageBreak.pluginName ]: PageBreak;
}
}
74 changes: 74 additions & 0 deletions packages/ckeditor5-page-break/src/pagebreakcommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* @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 page-break/pagebreakcommand
*/

import { Command } from 'ckeditor5/src/core';
import { findOptimalInsertionRange } from 'ckeditor5/src/widget';
import type { DocumentSelection, Element, Model, Schema } from 'ckeditor5/src/engine';

/**
* The page break command.
*
* The command is registered by {@link module:page-break/pagebreakediting~PageBreakEditing} as `'pageBreak'`.
*
* To insert a page break at the current selection, execute the command:
*
* editor.execute( 'pageBreak' );
*/
export default class PageBreakCommand extends Command {
/**
* @inheritDoc
*/
public override refresh(): void {
const model = this.editor.model;
const schema = model.schema;
const selection = model.document.selection;

this.isEnabled = isPageBreakAllowedInParent( selection, schema, model );
}

/**
* Executes the command.
*
* @fires execute
*/
public override execute(): void {
const model = this.editor.model;

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

model.insertObject( pageBreakElement, null, null, {
setSelection: 'after'
} );
} );
}
}

/**
* Checks if a page break is allowed by the schema in the optimal insertion parent.
*/
function isPageBreakAllowedInParent( selection: DocumentSelection, schema: Schema, model: Model ): boolean {
const parent = getInsertPageBreakParent( selection, model );

return schema.checkChild( parent, 'pageBreak' );
}

/**
* Returns a node that will be used to insert a page break with `model.insertContent` to check if the page break can be placed there.
*/
function getInsertPageBreakParent( selection: DocumentSelection, model: Model ): Element {
const insertionRange = findOptimalInsertionRange( selection, model );
const parent = insertionRange.start.parent;

if ( parent.isEmpty && !parent.is( 'element', '$root' ) ) {
return parent.parent as Element;
}

return parent as Element;
}
140 changes: 140 additions & 0 deletions packages/ckeditor5-page-break/src/pagebreakediting.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/**
* @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 page-break/pagebreakediting
*/

import { Plugin } from 'ckeditor5/src/core';
import { toWidget } from 'ckeditor5/src/widget';
import type { DowncastWriter, ViewElement } from 'ckeditor5/src/engine';

import PageBreakCommand from './pagebreakcommand';

import '../theme/pagebreak.css';

/**
* The page break editing feature.
*/
export default class PageBreakEditing extends Plugin {
/**
* @inheritDoc
*/
public static get pluginName(): 'PageBreakEditing' {
return 'PageBreakEditing';
}

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

schema.register( 'pageBreak', {
inheritAllFrom: '$blockObject'
} );

conversion.for( 'dataDowncast' ).elementToStructure( {
model: 'pageBreak',
view: ( modelElement, { writer } ) => {
const divElement = writer.createContainerElement( 'div',
{
class: 'page-break',
// If user has no `.ck-content` styles, it should always break a page during print.
style: 'page-break-after: always'
},
// For a rationale of using span inside a div see:
// https://github.com/ckeditor/ckeditor5-page-break/pull/1#discussion_r328934062.
writer.createContainerElement( 'span', {
style: 'display: none'
} )
);

return divElement;
}
} );

conversion.for( 'editingDowncast' ).elementToStructure( {
model: 'pageBreak',
view: ( modelElement, { writer } ) => {
const label = t( 'Page break' );
const viewWrapper = writer.createContainerElement( 'div' );
const viewLabelElement = writer.createRawElement(
'span',
{ class: 'page-break__label' },
function( domElement ) {
domElement.innerText = t( 'Page break' );
}
);

writer.addClass( 'page-break', viewWrapper );
writer.insert( writer.createPositionAt( viewWrapper, 0 ), viewLabelElement );

return toPageBreakWidget( viewWrapper, writer, label );
}
} );

conversion.for( 'upcast' )
.elementToElement( {
view: element => {
// For upcast conversion it's enough if we check for element style and verify if it's empty
// or contains only hidden span element.

const hasPageBreakBefore = element.getStyle( 'page-break-before' ) == 'always';
const hasPageBreakAfter = element.getStyle( 'page-break-after' ) == 'always';

if ( !hasPageBreakBefore && !hasPageBreakAfter ) {
return null;
}

// The "page break" div accepts only single child or no child at all.
if ( element.childCount == 1 ) {
const viewSpan = element.getChild( 0 );

// The child must be the "span" element that is not displayed.
if ( !viewSpan!.is( 'element', 'span' ) || viewSpan.getStyle( 'display' ) != 'none' ) {
return null;
}
} else if ( element.childCount > 1 ) {
return null;
}

return { name: true };
},
model: 'pageBreak',

// This conversion must be checked before <br> conversion because some editors use
// <br style="page-break-before:always"> as a page break marker.
converterPriority: 'high'
} );

editor.commands.add( 'pageBreak', new PageBreakCommand( editor ) );
}
}

/**
* Converts a given {@link module:engine/view/element~Element} to a page break widget:
* * Adds a {@link module:engine/view/element~Element#_setCustomProperty custom property} allowing to
* recognize the page break widget element.
* * Calls the {@link module:widget/utils~toWidget} function with the proper element's label creator.
*/
function toPageBreakWidget( viewElement: ViewElement, writer: DowncastWriter, label: string ): ViewElement {
writer.setCustomProperty( 'pageBreak', true, viewElement );

return toWidget( viewElement, writer, { label } );
}

declare module '@ckeditor/ckeditor5-core' {
interface CommandsMap {
pageBreak: PageBreakCommand;
}

interface PluginsMap {
[ PageBreakEditing.pluginName ]: PageBreakEditing;
}
}
61 changes: 61 additions & 0 deletions packages/ckeditor5-page-break/src/pagebreakui.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* @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 page-break/pagebreakui
*/

import { Plugin } from 'ckeditor5/src/core';
import { ButtonView } from 'ckeditor5/src/ui';

import pageBreakIcon from '../theme/icons/pagebreak.svg';

/**
* The page break UI plugin.
*/
export default class PageBreakUI extends Plugin {
/**
* @inheritDoc
*/
public static get pluginName(): 'PageBreakUI' {
return 'PageBreakUI';
}

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

// Add pageBreak button to feature components.
editor.ui.componentFactory.add( 'pageBreak', locale => {
const command = editor.commands.get( 'pageBreak' )!;
const view = new ButtonView( locale );

view.set( {
label: t( 'Page break' ),
icon: pageBreakIcon,
tooltip: true
} );

view.bind( 'isEnabled' ).to( command, 'isEnabled' );

// Execute command.
this.listenTo( view, 'execute', () => {
editor.execute( 'pageBreak' );
editor.editing.view.focus();
} );

return view;
} );
}
}

declare module '@ckeditor/ckeditor5-core' {
interface PluginsMap {
[ PageBreakUI.pluginName ]: PageBreakUI;
}
}
7 changes: 7 additions & 0 deletions packages/ckeditor5-page-break/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": "ckeditor5/tsconfig.json",
"include": [
"src",
"../../typings"
]
}
10 changes: 10 additions & 0 deletions packages/ckeditor5-page-break/tsconfig.release.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.release.json",
"include": [
"./src/",
"../../typings/"
],
"exclude": [
"./tests/"
]
}

0 comments on commit b88a562

Please sign in to comment.