|
|
@@ -27,9 +27,8 @@ export default class FontFamilyEditing extends Plugin {
|
|
|
constructor( editor ) {
|
|
|
super( editor );
|
|
|
|
|
|
- // Define default configuration using named presets
|
|
|
editor.config.define( 'fontFamily', {
|
|
|
- items: [
|
|
|
+ options: [
|
|
|
'default',
|
|
|
'Arial, Helvetica, sans-serif',
|
|
|
'Courier New, Courier, monospace',
|
|
|
@@ -41,46 +40,43 @@ export default class FontFamilyEditing extends Plugin {
|
|
|
'Verdana, Geneva, sans-serif'
|
|
|
]
|
|
|
} );
|
|
|
-
|
|
|
- // Get configuration
|
|
|
- const data = editor.data;
|
|
|
- const editing = editor.editing;
|
|
|
-
|
|
|
- // Add converters from view to model.
|
|
|
- const items = this.configuredItems.filter( item => item.model );
|
|
|
-
|
|
|
- for ( const item of items ) {
|
|
|
- viewToModelAttribute( 'fontFamily', item, [ data.viewToModel ] );
|
|
|
- }
|
|
|
-
|
|
|
- // Covert from model to view.
|
|
|
- modelAttributeToViewAttributeElement( 'fontFamily', items, [ data.modelToView, editing.modelToView ] );
|
|
|
-
|
|
|
- // Add FontSize command.
|
|
|
- editor.commands.add( 'fontFamily', new FontFamilyCommand( editor ) );
|
|
|
}
|
|
|
|
|
|
- get configuredItems() {
|
|
|
+ /**
|
|
|
+ * Returns {@link module:font/fontfamily/fontfamilyediting~FontFamilyConfig#options} array with options normalized in the
|
|
|
+ * {@link module:font/fontfamily/fontfamilyediting~FontFamilyOption} format, translated
|
|
|
+ * `title` for each style.
|
|
|
+ *
|
|
|
+ * @readonly
|
|
|
+ * @type {Array.<module:font/fontfamily/fontfamilyediting/imagestyleengine~ImageStyleFormat>}
|
|
|
+ */
|
|
|
+ get configuredOptions() {
|
|
|
// Cache value
|
|
|
- if ( this._cachedItems ) {
|
|
|
- return this._cachedItems;
|
|
|
+ if ( this._cachedOptions ) {
|
|
|
+ return this._cachedOptions;
|
|
|
}
|
|
|
|
|
|
- const items = [];
|
|
|
const editor = this.editor;
|
|
|
- const config = editor.config;
|
|
|
+ const t = editor.t;
|
|
|
|
|
|
- const configuredItems = config.get( 'fontFamily.items' );
|
|
|
+ const options = [];
|
|
|
+ const configuredOptions = editor.config.get( 'fontFamily.options' );
|
|
|
|
|
|
- for ( const item of configuredItems ) {
|
|
|
+ for ( const item of configuredOptions ) {
|
|
|
const itemDefinition = getItemDefinition( item );
|
|
|
|
|
|
+ // Set only valid definitions.
|
|
|
if ( itemDefinition ) {
|
|
|
- items.push( itemDefinition );
|
|
|
+ // Localize the "Default" title if set.
|
|
|
+ if ( itemDefinition.title === 'Default' ) {
|
|
|
+ itemDefinition.title = t( 'Default' );
|
|
|
+ }
|
|
|
+
|
|
|
+ options.push( itemDefinition );
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return ( this._cachedItems = items );
|
|
|
+ return ( this._cachedOptions = options );
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -88,29 +84,45 @@ export default class FontFamilyEditing extends Plugin {
|
|
|
*/
|
|
|
init() {
|
|
|
const editor = this.editor;
|
|
|
+ const data = editor.data;
|
|
|
+ const editing = editor.editing;
|
|
|
|
|
|
// Allow highlight attribute on all elements
|
|
|
editor.model.schema.allow( { name: '$inline', attributes: 'fontFamily', inside: '$block' } );
|
|
|
// Temporary workaround. See https://github.com/ckeditor/ckeditor5/issues/477.
|
|
|
editor.model.schema.allow( { name: '$inline', attributes: 'fontFamily', inside: '$clipboardHolder' } );
|
|
|
+
|
|
|
+ // Get configured font family options without "default" option.
|
|
|
+ const fontFamilyOptions = this.configuredOptions.filter( item => item.model );
|
|
|
+
|
|
|
+ // Define view to model conversion.
|
|
|
+ for ( const item of fontFamilyOptions ) {
|
|
|
+ viewToModelAttribute( 'fontFamily', item, [ data.viewToModel ] );
|
|
|
+ }
|
|
|
+
|
|
|
+ // Define model to view conversion.
|
|
|
+ modelAttributeToViewAttributeElement( 'fontFamily', fontFamilyOptions, [ data.modelToView, editing.modelToView ] );
|
|
|
+
|
|
|
+ editor.commands.add( 'fontFamily', new FontFamilyCommand( editor ) );
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Returns item definition from preset
|
|
|
function getItemDefinition( item ) {
|
|
|
- // Probably it is full item definition so return it
|
|
|
+ // Probably it is full item definition so return it.
|
|
|
if ( typeof item === 'object' ) {
|
|
|
return item;
|
|
|
}
|
|
|
|
|
|
+ // Handle 'default' string as a special case. It will be used to remove the fontFamily attribute.
|
|
|
if ( item === 'default' ) {
|
|
|
return {
|
|
|
- title: 'Default', // TODO localize us
|
|
|
+ title: 'Default',
|
|
|
model: undefined
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- // Ignore falsy values
|
|
|
+ // Ignore values that we cannot parse to a definition.
|
|
|
if ( typeof item !== 'string' ) {
|
|
|
return;
|
|
|
}
|
|
|
@@ -118,21 +130,33 @@ function getItemDefinition( item ) {
|
|
|
return generateFontPreset( item );
|
|
|
}
|
|
|
|
|
|
-// Creates a predefined preset for pixel size.
|
|
|
-function generateFontPreset( font ) {
|
|
|
- // Remove quotes from font names
|
|
|
- const fontNames = font.replace( /"|'/g, '' ).split( ',' );
|
|
|
-
|
|
|
- const cssFontNames = fontNames.map( normalizeFontName );
|
|
|
+// Creates a predefined preset for pixel size. It deconstructs font-family like string into full configuration option.
|
|
|
+// A font definition is passed as coma delimited set of font family names. Font names might be quoted.
|
|
|
+//
|
|
|
+// @param {String} A font definition form configuration.
|
|
|
+function generateFontPreset( fontDefinition ) {
|
|
|
+ // Remove quotes from font names - will be normalized later.
|
|
|
+ const fontNames = fontDefinition.replace( /"|'/g, '' ).split( ',' );
|
|
|
|
|
|
+ // The first matched font name will be used as dropdown list item title and as model value
|
|
|
const firstFontName = fontNames[ 0 ];
|
|
|
|
|
|
- const quotesMatch = '("|\'|&qout;|\\W){0,2}';
|
|
|
+ const cssFontNames = fontNames.map( normalizeFontName );
|
|
|
|
|
|
+ // TODO: Maybe we can come with something better here?
|
|
|
+ // TODO: Also document this behavior in engine as it uses matcher~Pattern not ViewElementDefinition.
|
|
|
+ // TODO: Maybe a better solution will be a callback here? (also needs documentation)
|
|
|
+ // This will match any quote type with whitespace.
|
|
|
+ const quotesMatch = '("|\'|&qout;|\\W){0,2}';
|
|
|
+ // Full regex will catch any style of quotation used in view.
|
|
|
+ // Example:
|
|
|
+ // from string: "Font Foo Foo, Font Bar"
|
|
|
+ // it will create a regex that will match any quotation mix:
|
|
|
+ // - "Font Foo Foo", Font Bar
|
|
|
+ // - 'Font Foo Foo', "Font Bar"
|
|
|
+ // - ... etc.
|
|
|
const regexString = `${ quotesMatch }${ fontNames.map( n => n.trim() ).join( `${ quotesMatch },${ quotesMatch }` ) }${ quotesMatch }`;
|
|
|
|
|
|
- const fontFamilyRegexp = new RegExp( regexString );
|
|
|
-
|
|
|
return {
|
|
|
title: firstFontName,
|
|
|
model: firstFontName,
|
|
|
@@ -146,21 +170,21 @@ function generateFontPreset( font ) {
|
|
|
{
|
|
|
name: 'span',
|
|
|
style: {
|
|
|
- 'font-family': fontFamilyRegexp
|
|
|
+ 'font-family': new RegExp( regexString )
|
|
|
}
|
|
|
}
|
|
|
]
|
|
|
};
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- *
|
|
|
- * @param fontName
|
|
|
- * @returns {string|*}
|
|
|
- */
|
|
|
+// Normalizes font name for the view style attribute.
|
|
|
+//
|
|
|
+// @param {String} fontName
|
|
|
+// @returns {String}
|
|
|
function normalizeFontName( fontName ) {
|
|
|
fontName = fontName.trim();
|
|
|
|
|
|
+ // Compound font names should be quoted.
|
|
|
if ( fontName.indexOf( ' ' ) > 0 ) {
|
|
|
fontName = `'${ fontName }'`;
|
|
|
}
|
|
|
@@ -169,17 +193,22 @@ function normalizeFontName( fontName ) {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Font family option descriptor.
|
|
|
+ * Font family option. Compatible with {@link module:engine/conversion/definition-based-converters~ConverterDefinition}.
|
|
|
*
|
|
|
* @typedef {Object} module:font/fontfamily/fontfamilyediting~FontFamilyOption
|
|
|
*
|
|
|
- * @property {String} title TODO me
|
|
|
- * @property {String} model TODO me
|
|
|
- * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view View element configuration.
|
|
|
+ * @property {String} model The `fontFamily` attribute value in the model.
|
|
|
+ * @property {module:engine/view/viewelementdefinition~ViewElementDefinition} view The view representation for that option.
|
|
|
+ * @property {String} title The user-readable title of the option.
|
|
|
+ * @property {String} [uiStyle] The style which will be added to the dropdown item representing this option.
|
|
|
+ * Defaults to `view.style[ 'font-family' ]`.
|
|
|
+ * @property {Array.<module:engine/view/viewelementdefinition~ViewElementDefinition>} acceptAlso An array with all matched elements that
|
|
|
+ * view to model conversion should also accept.
|
|
|
*/
|
|
|
|
|
|
/**
|
|
|
- * The configuration of the heading feature. Introduced by the {@link module:font/fontfamily/fontfamilyediting~FontSizeEditing} feature.
|
|
|
+ * The configuration of the font family feature.
|
|
|
+ * Introduced by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} feature.
|
|
|
*
|
|
|
* Read more in {@link module:font/fontfamily/fontfamilyediting~FontFamilyConfig}.
|
|
|
*
|
|
|
@@ -188,14 +217,14 @@ function normalizeFontName( fontName ) {
|
|
|
|
|
|
/**
|
|
|
* The configuration of the font family feature.
|
|
|
- * The option is used by the {@link module:font/fontfamily/fontfamilyediting~FontSizeEditing} feature.
|
|
|
+ * The option is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} feature.
|
|
|
*
|
|
|
- * ClassicEditor
|
|
|
- * .create( {
|
|
|
+ * ClassicEditor
|
|
|
+ * .create( {
|
|
|
* fontFamily: ... // Font family feature config.
|
|
|
* } )
|
|
|
- * .then( ... )
|
|
|
- * .catch( ... );
|
|
|
+ * .then( ... )
|
|
|
+ * .catch( ... );
|
|
|
*
|
|
|
* See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
|
|
|
*
|
|
|
@@ -206,9 +235,27 @@ function normalizeFontName( fontName ) {
|
|
|
* Available font family options. Defined either as array of strings.
|
|
|
*
|
|
|
* The default value is
|
|
|
- * TODO code
|
|
|
- * which configures
|
|
|
*
|
|
|
- * @member {Array.<String|Number|module:font/fontfamily/fontfamilyediting~FontFamilyOption>}
|
|
|
- * module:font/fontfamily/fontfamilyediting~FontFamilyConfig#items
|
|
|
+ * const fontFamilyConfig = {
|
|
|
+ * options: [
|
|
|
+ * 'default',
|
|
|
+ * 'Arial, Helvetica, sans-serif',
|
|
|
+ * 'Courier New, Courier, monospace',
|
|
|
+ * 'Georgia, serif',
|
|
|
+ * 'Lucida Sans Unicode, Lucida Grande, sans-serif',
|
|
|
+ * 'Tahoma, Geneva, sans-serif',
|
|
|
+ * 'Times New Roman, Times, serif',
|
|
|
+ * 'Trebuchet MS, Helvetica, sans-serif',
|
|
|
+ * 'Verdana, Geneva, sans-serif'
|
|
|
+ * ]
|
|
|
+ * };
|
|
|
+ *
|
|
|
+ * which configures 8 font family options and a default option that will remove font family to text default setting (defaulting to content
|
|
|
+ * CSS styles).
|
|
|
+ *
|
|
|
+ * TODO: what 'default' does.
|
|
|
+ * TODO: how those string are translated to configuration
|
|
|
+ *
|
|
|
+ * @member {Array.<String|module:font/fontfamily/fontfamilyediting~FontFamilyOption>}
|
|
|
+ * module:font/fontfamily/fontfamilyediting~FontFamilyConfig#options
|
|
|
*/
|