浏览代码

Other: Initial implementation of FontSize configuration and editing pipeline conversion.

Maciej Gołaszewski 8 年之前
父节点
当前提交
14b93b96da

+ 156 - 1
packages/ckeditor5-font/src/fontsize/fontsizeediting.js

@@ -8,6 +8,8 @@
  */
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import buildModelConverter from '@ckeditor/ckeditor5-engine/src/conversion/buildmodelconverter';
+import AttributeElement from '@ckeditor/ckeditor5-engine/src/view/attributeelement';
 
 /**
  * The Font Size Editing feature.
@@ -21,7 +23,69 @@ export default class FontSizeEditing extends Plugin {
 	constructor( editor ) {
 		super( editor );
 
-		editor.config.define( 'fontSize', { items: [] } );
+		// Define default configuration using named presets
+		editor.config.define( 'fontSize', {
+			items: [
+				'tiny',
+				'small',
+				'normal',
+				'big',
+				'huge'
+			]
+		} );
+
+		// Get configuration
+		const data = editor.data;
+		const editing = editor.editing;
+
+		// Convert model to view
+		buildModelConverter().for( data.modelToView, editing.modelToView )
+			.fromAttribute( 'fontSize' )
+			.toElement( data => {
+				const definition = this._getDefinition( data );
+
+				if ( !definition ) {
+					return;
+				}
+
+				// TODO: make utitlity class of this?
+				const viewDefinition = definition.view;
+
+				const attributes = {};
+
+				if ( viewDefinition.classes ) {
+					attributes.class = viewDefinition.classes;
+				}
+
+				if ( viewDefinition.styles ) {
+					attributes.style = viewDefinition.styles;
+				}
+
+				return new AttributeElement( viewDefinition.name, attributes );
+			} );
+	}
+
+	get configuredItems() {
+		// Cache value
+		if ( this._cachedItems ) {
+			return this._cachedItems;
+		}
+
+		const items = [];
+		const editor = this.editor;
+		const config = editor.config;
+
+		const configuredItems = config.get( 'fontSize.items' );
+
+		for ( const item of configuredItems ) {
+			const itemDefinition = getItemDefinition( item );
+
+			if ( itemDefinition ) {
+				items.push( itemDefinition );
+			}
+		}
+
+		return ( this._cachedItems = items );
 	}
 
 	/**
@@ -35,4 +99,95 @@ export default class FontSizeEditing extends Plugin {
 		// Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477.
 		editor.document.schema.allow( { name: '$inline', attributes: 'fontSize', inside: '$clipboardHolder' } );
 	}
+
+	_getDefinition( name ) {
+		const stringName = String( name );
+
+		for ( const item of this.configuredItems ) {
+			if ( item.model === stringName ) {
+				return item;
+			}
+		}
+	}
+}
+
+const namedPresets = {
+	tiny: {
+		label: 'Tiny',
+		model: 'text-tiny',
+		// stopValue: .7
+		// stopUnit: 'em'
+		view: {
+			name: 'span',
+			classes: 'text-tiny'
+		}
+	},
+	small: {
+		label: 'Small',
+		model: 'text-small',
+		// stopValue: .85
+		// stopUnit: 'em',
+		view: {
+			name: 'span',
+			classes: 'text-small'
+		}
+	},
+	big: {
+		label: 'Big',
+		model: 'text-big',
+		// stopValue: 1.4
+		// stopUnit: 'em',
+		view: {
+			name: 'span',
+			classes: 'text-big'
+		}
+	},
+	huge: {
+		label: 'Huge',
+		model: 'text-huge',
+		// stopValue: 1.8
+		// stopUnit: 'em',
+		view: {
+			name: 'span',
+			classes: 'text-huge'
+		}
+	}
+};
+
+// Returns item definition from preset
+function getItemDefinition( item ) {
+	// Named preset exist so return it
+	if ( namedPresets[ item ] ) {
+		return namedPresets[ item ];
+	}
+
+	// Probably it is full item definition so return it
+	if ( typeof item === 'object' ) {
+		return item;
+	}
+
+	// At this stage we probably have numerical value to generate a preset so parse it's value.
+	const sizePreset = parseInt( item ); // TODO: Should we parse floats? 🤔
+
+	// Discard any faulty values.
+	if ( isNaN( sizePreset ) ) {
+		return;
+	}
+
+	return generatePixelPreset( sizePreset );
+}
+
+// Creates a predefined preset for pixel size.
+function generatePixelPreset( size ) {
+	const sizeName = String( size );
+
+	return {
+		label: sizeName,
+		model: sizeName,
+		stopValue: size,
+		view: {
+			name: 'span',
+			styles: `font-size: ${ size }px`
+		}
+	};
 }

+ 141 - 1
packages/ckeditor5-font/tests/fontsize/fontsizeediting.js

@@ -8,6 +8,7 @@ import FontSizeEditing from './../../src/fontsize/fontsizeediting';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
+import { setData as setModelData } from '../../../ckeditor5-engine/src/dev-utils/model';
 
 describe( 'FontSizeEditing', () => {
 	let editor, doc;
@@ -36,8 +37,147 @@ describe( 'FontSizeEditing', () => {
 	describe( 'config', () => {
 		describe( 'default value', () => {
 			it( 'should be set', () => {
-				expect( editor.config.get( 'fontSize.items' ) ).to.deep.equal( [] );
+				expect( editor.config.get( 'fontSize.items' ) ).to.deep.equal( [ 'tiny', 'small', 'normal', 'big', 'huge' ] );
 			} );
 		} );
 	} );
+
+	describe( 'configuredItems', () => {
+		it( 'should discard falsy values', () => {
+			return VirtualTestEditor
+				.create( {
+					plugins: [ FontSizeEditing ],
+					fontSize: {
+						items: [ () => {}, 'normal', 'unknown' ]
+					}
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+
+					const plugin = editor.plugins.get( FontSizeEditing );
+
+					expect( plugin.configuredItems ).to.deep.equal( [] );
+				} );
+		} );
+
+		it( 'should pass through object definition', () => {
+			return VirtualTestEditor
+				.create( {
+					plugins: [ FontSizeEditing ],
+					fontSize: {
+						items: [ { label: 'My Size', model: 'my-size', view: { name: 'span', styles: 'font-size: 12em;' } } ]
+					}
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+
+					const plugin = editor.plugins.get( FontSizeEditing );
+
+					expect( plugin.configuredItems ).to.deep.equal( [
+						{
+							label: 'My Size',
+							model: 'my-size',
+							view: { name: 'span', styles: 'font-size: 12em;' }
+						}
+					] );
+				} );
+		} );
+
+		describe( 'named presets', () => {
+			it( 'should return defined presets', () => {
+				return VirtualTestEditor
+					.create( {
+						plugins: [ FontSizeEditing ],
+						fontSize: {
+							items: [ 'tiny', 'small', 'normal', 'big', 'huge' ]
+						}
+					} )
+					.then( newEditor => {
+						editor = newEditor;
+
+						const plugin = editor.plugins.get( FontSizeEditing );
+
+						expect( plugin.configuredItems ).to.deep.equal( [
+							{ label: 'Tiny', model: 'text-tiny', view: { name: 'span', classes: 'text-tiny' } },
+							{ label: 'Small', model: 'text-small', view: { name: 'span', classes: 'text-small' } },
+							{ label: 'Big', model: 'text-big', view: { name: 'span', classes: 'text-big' } },
+							{ label: 'Huge', model: 'text-huge', view: { name: 'span', classes: 'text-huge' } }
+						] );
+					} );
+			} );
+		} );
+
+		describe( 'numeric presets', () => {
+			it( 'should return generated presets', () => {
+				return VirtualTestEditor
+					.create( {
+						plugins: [ FontSizeEditing ],
+						fontSize: {
+							items: [ '10', 12, 'normal', '14.1', 18.3 ]
+						}
+					} )
+					.then( newEditor => {
+						editor = newEditor;
+
+						const plugin = editor.plugins.get( FontSizeEditing );
+
+						expect( plugin.configuredItems ).to.deep.equal( [
+							{ label: '10', model: '10', stopValue: 10, view: { name: 'span', styles: 'font-size: 10px' } },
+							{ label: '12', model: '12', stopValue: 12, view: { name: 'span', styles: 'font-size: 12px' } },
+							{ label: '14', model: '14', stopValue: 14, view: { name: 'span', styles: 'font-size: 14px' } },
+							{ label: '18', model: '18', stopValue: 18, view: { name: 'span', styles: 'font-size: 18px' } }
+						] );
+					} );
+			} );
+		} );
+	} );
+
+	describe( 'editing pipeline conversion', () => {
+		beforeEach( () => {
+			return VirtualTestEditor
+				.create( {
+					plugins: [ FontSizeEditing, Paragraph ],
+					fontSize: {
+						items: [ 'tiny', 'normal', 18, {
+							label: 'My setting',
+							model: 'my',
+							view: {
+								name: 'mark',
+								styles: 'font-size: 30px',
+								classes: 'my-style'
+							}
+						} ]
+					}
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+
+					doc = editor.document;
+				} );
+		} );
+
+		it( 'should discard unknown fontSize attribute values', () => {
+			setModelData( doc, '<paragraph>f<$text fontSize="foo-bar">o</$text>o</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p>foo</p>' );
+		} );
+
+		it( 'should convert fontSize attribute to predefined named preset', () => {
+			setModelData( doc, '<paragraph>f<$text fontSize="text-tiny">o</$text>o</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p>f<span class="text-tiny">o</span>o</p>' );
+		} );
+
+		it( 'should convert fontSize attribute to predefined pixel size preset', () => {
+			setModelData( doc, '<paragraph>f<$text fontSize="18">o</$text>o</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p>f<span style="font-size:18px;">o</span>o</p>' );
+		} );
+
+		it( 'should convert fontSize attribute from user defined settings', () => {
+			setModelData( doc, '<paragraph>f<$text fontSize="my">o</$text>o</paragraph>' );
+
+			expect( editor.getData() ).to.equal( '<p>f<mark class="my-style" style="font-size:30px;">o</mark>o</p>' );
+		} );
+	} );
 } );