Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Other: Update naming of UI components & commands. #99

Merged
merged 9 commits into from
Mar 11, 2018
8 changes: 4 additions & 4 deletions docs/features/headings.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ import Heading from '@ckeditor/ckeditor5-heading/src/heading';
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [ Heading, ... ],
toolbar: [ 'headings', ... ]
toolbar: [ 'heading', ... ]
} )
.then( ... )
.catch( ... );
Expand All @@ -132,13 +132,13 @@ ClassicEditor

The {@link module:heading/heading~Heading} plugin registers:

* The `'headings'` dropdown.
* The `'heading1'`, `'heading2'`, ..., `'headingN'` commands based on the {@link module:heading/heading~HeadingConfig#options `heading.options`} configuration option.
* The `'heading'` dropdown.
* The `'heading'` command that accepts value based on the {@link module:heading/heading~HeadingConfig#options `heading.options`} configuration option.

You can turn the currently selected block(s) to headings by executing one of these commands:

```js
editor.execute( 'heading2' );
editor.execute( 'heading', { value: 'heading2' } );
```

## Contribute
Expand Down
4 changes: 2 additions & 2 deletions src/heading.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,10 @@ export default class Heading extends Plugin {
* That's assumption is used by features like {@link module:autoformat/autoformat~Autoformat} to know which element
* they should use when applying the first level heading.
*
* The defined headings are also available in {@link module:core/commandcollection~CommandCollection} under their model names.
* The defined headings are also available as values passed to `heading` command under their model names.
* For example, the below code will apply `<heading1>` to the current selection:
*
* editor.execute( 'heading1' );
* editor.execute( 'heading', { value: 'heading1' } );
*
* @member {Array.<module:heading/heading~HeadingOption>} module:heading/heading~HeadingConfig#options
*/
Expand Down
36 changes: 23 additions & 13 deletions src/headingcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,29 @@ export default class HeadingCommand extends Command {
* Creates an instance of the command.
*
* @param {module:core/editor/editor~Editor} editor Editor instance.
* @param {String} modelElement Name of the element which this command will apply in the model.
* @param {Array.<String>} modelElements Names of the element which this command can apply in the model.
*/
constructor( editor, modelElement ) {
constructor( editor, modelElements ) {
super( editor );

/**
* Whether the selection starts in a heading of {@link #modelElement this level}.
* If the selection starts in a heading (which {@link #modelElements is supported by this command})
* the value is set to the name of that heading model element.
* It is set to `false` otherwise.
*
* @observable
* @readonly
* @member {Boolean} #value
* @member {Boolean|String} #value
*/

/**
* Unique identifier of the command, also element's name in the model.
* Set of defined model's elements names that this command support.
* See {@link module:heading/heading~HeadingOption}.
*
* @readonly
* @member {String}
* @member {Array.<String>}
*/
this.modelElement = modelElement;
this.modelElements = modelElements;
}

/**
Expand All @@ -49,29 +51,37 @@ export default class HeadingCommand extends Command {
refresh() {
const block = first( this.editor.model.document.selection.getSelectedBlocks() );

this.value = !!block && block.is( this.modelElement );
this.isEnabled = !!block && checkCanBecomeHeading( block, this.modelElement, this.editor.model.schema );
this.value = !!block && this.modelElements.includes( block.name ) && block.name;
this.isEnabled = !!block && this.modelElements.some( heading => checkCanBecomeHeading( block, heading, this.editor.model.schema ) );
}

/**
* Executes the command. Applies the heading to the selected blocks or, if the first selected
* block is a heading already, turns selected headings (of this level only) to paragraphs.
*
* @param {Object} options
* @param {String} options.value Name of the element which this command will apply in the model.
* @fires execute
*/
execute() {
execute( options = {} ) {
const model = this.editor.model;
const document = model.document;

const modelElement = options.value;

if ( !this.modelElements.includes( modelElement ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such checks cause silent errors. Throw explicitly or let app throw at some point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reported a followup for this: ckeditor/ckeditor5#890

return;
}

model.change( writer => {
const blocks = Array.from( document.selection.getSelectedBlocks() )
.filter( block => {
return checkCanBecomeHeading( block, this.modelElement, model.schema );
return checkCanBecomeHeading( block, modelElement, model.schema );
} );

for ( const block of blocks ) {
if ( !block.is( this.modelElement ) ) {
writer.rename( block, this.modelElement );
if ( !block.is( modelElement ) ) {
writer.rename( block, modelElement );
}
}
} );
Expand Down
8 changes: 6 additions & 2 deletions src/headingediting.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ export default class HeadingEditing extends Plugin {
const editor = this.editor;
const options = editor.config.get( 'heading.options' );

const modelElements = [];

for ( const option of options ) {
// Skip paragraph - it is defined in required Paragraph feature.
if ( option.model !== defaultModelElement ) {
Expand All @@ -61,10 +63,12 @@ export default class HeadingEditing extends Plugin {

editor.conversion.elementToElement( option );

// Register the heading command for this option.
editor.commands.add( option.model, new HeadingCommand( editor, option.model ) );
modelElements.push( option.model );
}
}

// Register the heading command for this option.
editor.commands.add( 'heading', new HeadingCommand( editor, modelElements ) );
}

/**
Expand Down
34 changes: 23 additions & 11 deletions src/headingui.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,37 @@ export default class HeadingUI extends Plugin {
const dropdownTooltip = t( 'Heading' );

// Register UI component.
editor.ui.componentFactory.add( 'headings', locale => {
const commands = [];
editor.ui.componentFactory.add( 'heading', locale => {
const titles = {};
const dropdownItems = new Collection();

const headingCommand = editor.commands.get( 'heading' );
const paragraphCommand = editor.commands.get( 'paragraph' );

const commands = [ headingCommand ];

for ( const option of options ) {
const command = editor.commands.get( option.model );
const itemModel = new Model( {
commandName: option.model,
label: option.title,
class: option.class
} );

itemModel.bind( 'isActive' ).to( command, 'value' );
if ( option.model === 'paragraph' ) {
itemModel.bind( 'isActive' ).to( paragraphCommand, 'value' );
itemModel.set( 'commandName', 'paragraph' );
commands.push( paragraphCommand );
} else {
itemModel.bind( 'isActive' ).to( headingCommand, 'value', value => value === option.model );
itemModel.set( {
commandName: 'heading',
commandValue: option.model
} );
}

// Add the option to the collection.
dropdownItems.add( itemModel );

commands.push( command );
titles[ option.model ] = option.title;
}

const dropdownView = createDropdown( locale );
Expand All @@ -74,16 +87,15 @@ export default class HeadingUI extends Plugin {
return areEnabled.some( isEnabled => isEnabled );
} );

dropdownView.buttonView.bind( 'label' ).toMany( commands, 'value', ( ...areActive ) => {
const index = areActive.findIndex( value => value );

dropdownView.buttonView.bind( 'label' ).to( headingCommand, 'value', paragraphCommand, 'value', ( value, para ) => {
const whichModel = value || para && 'paragraph';
// If none of the commands is active, display default title.
return options[ index ] ? options[ index ].title : defaultTitle;
return titles[ whichModel ] ? titles[ whichModel ] : defaultTitle;
} );

// Execute command when an item from the dropdown is selected.
this.listenTo( dropdownView, 'execute', evt => {
editor.execute( evt.source.commandName );
editor.execute( evt.source.commandName, evt.source.commandValue ? { value: evt.source.commandValue } : undefined );
editor.editing.view.focus();
} );

Expand Down
Loading