8
0
Kamil Piechaczek 6 лет назад
Родитель
Сommit
8c80e6037f

+ 3 - 0
packages/ckeditor5-page-break/lang/contexts.json

@@ -0,0 +1,3 @@
+{
+	"Page break": "Page break"
+}

+ 33 - 0
packages/ckeditor5-page-break/src/pagebreak.js

@@ -0,0 +1,33 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module page-break/pagebreak
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import PageBreakEditing from './pagebreakediting';
+import PageBreakUI from './pagebreakui';
+
+/**
+ * The page break plugin provides a possibility to insert a page break in the rich-text editor.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class PageBreak extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get requires() {
+		return [ PageBreakEditing, PageBreakUI ];
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'PageBreak';
+	}
+}

+ 98 - 0
packages/ckeditor5-page-break/src/pagebreakcommand.js

@@ -0,0 +1,98 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module page-break/pagebreakcommand
+ */
+
+import Command from '@ckeditor/ckeditor5-core/src/command';
+import { findOptimalInsertionPosition } from '@ckeditor/ckeditor5-widget/src/utils';
+
+/**
+ * The insert a page break command.
+ *
+ * The command is registered by the {@link module:page-break/pagebreakediting~PageBreakEditing} as `'pageBreak'`.
+ *
+ * To insert the page break at the current selection, execute the command:
+ *
+ *		editor.execute( 'pageBreak' );
+ *
+ * @extends module:core/command~Command
+ */
+export default class PageBreakCommand extends Command {
+	/**
+	 * @inheritDoc
+	 */
+	refresh() {
+		this.isEnabled = isPageBreakAllowed( this.editor.model );
+	}
+
+	/**
+	 * Executes the command.
+	 *
+	 * @fires execute
+	 */
+	execute() {
+		const model = this.editor.model;
+
+		model.change( writer => {
+			const modelElement = writer.createElement( 'pageBreak' );
+
+			model.insertContent( modelElement );
+		} );
+	}
+}
+
+// Checks if the `pageBreak` element can be inserted at current model selection.
+//
+// @param {module:engine/model/model~Model} model
+// @returns {Boolean}
+function isPageBreakAllowed( model ) {
+	const schema = model.schema;
+	const selection = model.document.selection;
+
+	return isPageBreakAllowedInParent( selection, schema, model ) &&
+		!checkSelectionOnObject( selection, schema );
+}
+
+// Checks if page break is allowed by schema in optimal insertion parent.
+//
+// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
+// @param {module:engine/model/schema~Schema} schema
+// @param {module:engine/model/model~Model} model Model instance.
+// @returns {Boolean}
+function isPageBreakAllowedInParent( selection, schema, model ) {
+	const parent = getInsertPageBreakParent( selection, model );
+
+	return schema.checkChild( parent, 'pageBreak' );
+}
+
+// Check if selection is on object.
+//
+// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
+// @param {module:engine/model/schema~Schema} schema
+// @returns {Boolean}
+function checkSelectionOnObject( selection, schema ) {
+	const selectedElement = selection.getSelectedElement();
+
+	return selectedElement && schema.isObject( selectedElement );
+}
+
+// Returns a node that will be used to insert page break with `model.insertContent` to check if page break can be placed there.
+//
+// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
+// @param {module:engine/model/model~Model} model Model instance.
+// @returns {module:engine/model/element~Element}
+function getInsertPageBreakParent( selection, model ) {
+	const insertAt = findOptimalInsertionPosition( selection, model );
+
+	const parent = insertAt.parent;
+
+	if ( parent.isEmpty && !parent.is( '$root' ) ) {
+		return parent.parent;
+	}
+
+	return parent;
+}

+ 104 - 0
packages/ckeditor5-page-break/src/pagebreakediting.js

@@ -0,0 +1,104 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module page-break/pagebreakediting
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import PageBreakCommand from './pagebreakcommand';
+import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
+import first from '@ckeditor/ckeditor5-utils/src/first';
+
+import '../theme/pagebreak.css';
+
+/**
+ * The page break editing feature.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class PageBreakEditing extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		const editor = this.editor;
+		const schema = editor.model.schema;
+		const t = editor.t;
+		const conversion = editor.conversion;
+
+		schema.register( 'pageBreak', {
+			isObject: true,
+			allowWhere: '$block'
+		} );
+
+		conversion.for( 'dataDowncast' ).elementToElement( {
+			model: 'pageBreak',
+			view: ( modelElement, viewWriter ) => {
+				const divElement = viewWriter.createContainerElement( 'div', {
+					style: 'page-break-after: always'
+				} );
+
+				const spanElement = viewWriter.createContainerElement( 'span', {
+					style: 'display: none'
+				} );
+
+				viewWriter.insert( viewWriter.createPositionAt( divElement, 0 ), spanElement );
+
+				return divElement;
+			}
+		} );
+
+		conversion.for( 'editingDowncast' ).elementToElement( {
+			model: 'pageBreak',
+			view: ( modelElement, viewWriter ) => {
+				const label = t( 'Page break' );
+				const viewWrapper = viewWriter.createContainerElement( 'div' );
+
+				viewWriter.addClass( 'ck-page-break', viewWrapper );
+				viewWriter.setCustomProperty( 'pageBreak', true, viewWrapper );
+
+				return toPageBreakWidget( viewWrapper, viewWriter, label );
+			}
+		} );
+
+		conversion.for( 'upcast' )
+			.elementToElement( {
+				view: element => {
+					// The "page break" div must have specified value for the 'page-break-after' definition and single child only.
+					if ( !element.is( 'div' ) || element.getStyle( 'page-break-after' ) != 'always' || element.childCount != 1 ) {
+						return;
+					}
+
+					const viewSpan = first( element.getChildren() );
+
+					// The child must be the "span" element that is not displayed and doesn't have any child.
+					if ( !viewSpan.is( 'span' ) || viewSpan.getStyle( 'display' ) != 'none' || viewSpan.childCount != 0 ) {
+						return;
+					}
+
+					return { name: true };
+				},
+				model: 'pageBreak'
+			} );
+
+		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.
+//
+//  @param {module:engine/view/element~Element} viewElement
+//  @param {module:engine/view/downcastwriter~DowncastWriter} writer An instance of the view writer.
+//  @param {String} label The element's label.
+//  @returns {module:engine/view/element~Element}
+function toPageBreakWidget( viewElement, writer, label ) {
+	writer.setCustomProperty( 'pageBreak', true, viewElement );
+
+	return toWidget( viewElement, writer, { label } );
+}

+ 44 - 0
packages/ckeditor5-page-break/src/pagebreakui.js

@@ -0,0 +1,44 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module page-break/pagebreakui
+ */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
+import pageBreakIcon from '../theme/icons/pagebreak.svg';
+
+/**
+ * The page break UI plugin.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class PageBreakUI extends Plugin {
+	init() {
+		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,
+				isToggleable: true
+			} );
+
+			view.bind( 'isEnabled' ).to( command, 'isEnabled' );
+
+			// Execute command.
+			this.listenTo( view, 'execute', () => editor.execute( 'pageBreak' ) );
+
+			return view;
+		} );
+	}
+}

+ 1 - 0
packages/ckeditor5-page-break/theme/icons/pagebreak.svg

@@ -0,0 +1 @@
+<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M3.598.6866h1.5v5h-1.5zm14.5 0h1.5v5h-1.5z"/><path d="M19.598 4.1866v1.5h-16v-1.5zm-16 14.5694h1.5v-5h-1.5zm14.5 0h1.5v-5h-1.5z"/><path d="M19.598 15.256v-1.5h-16v1.5zM5.0812 9h6v2h-6zm8 0h6v2h-6zM3.598 10L0 12.5v-5z"/></svg>

+ 14 - 0
packages/ckeditor5-page-break/theme/pagebreak.css

@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+.ck .ck-page-break {
+	width: calc( 100% - 1.2em );
+	padding: 6px 0;
+	margin-left: auto;
+	margin-right: auto;
+	border: 2px dashed var(--ck-color-toolbar-border);
+	border-left: 0;
+	border-right: 0;
+}