Skip to content

Commit

Permalink
Application menu bar
Browse files Browse the repository at this point in the history
Feature: Implemented the application menu bar that contains various options and commands for controlling and navigating the editor. Closes #15894.

MINOR BREAKING CHANGE (upload): The `FileDialogButtonView` class has been moved from `ckeditor5-upload` to `ckeditor5-ui`. Please update your import paths accordingly (was: `import { FileDialogButtonView } from 'ckeditor5/src/upload.js';`, is: `import { FileDialogButtonView } from 'ckeditor5/src/ui.js';`).

MINOR BREAKING CHANGE (theme-lark): The default vertical spacing around `ButtonView` in `ListItemView` (`--ck-list-button-padding`) has been reduced for better readability. This affects the presentation of various editor features that use this type of UI (headings, font size, font family, etc.). You can restore the previous value by setting `--ck-list-button-padding: calc(.2 * var(--ck-line-height-base) * var(--ck-font-size-base)) calc(.4 * var(--ck-line-height-base) * var(--ck-font-size-base));` in your custom styles sheet.
  • Loading branch information
oleq authored Apr 2, 2024
1 parent 0c64f2a commit f072048
Show file tree
Hide file tree
Showing 153 changed files with 14,673 additions and 2,952 deletions.
163 changes: 122 additions & 41 deletions packages/ckeditor5-alignment/src/alignmentui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,20 @@
*/

import { Plugin, icons } from 'ckeditor5/src/core.js';
import { ButtonView, createDropdown, addToolbarToDropdown, type Button } from 'ckeditor5/src/ui.js';
import {
type Button,
ButtonView,
createDropdown,
addToolbarToDropdown,
MenuBarMenuListItemView,
MenuBarMenuListItemButtonView,
MenuBarMenuView,
MenuBarMenuListView
} from 'ckeditor5/src/ui.js';
import type { Locale } from 'ckeditor5/src/utils.js';

import { isSupported, normalizeAlignmentOptions } from './utils.js';
import type { SupportedOption } from './alignmentconfig.js';
import type { AlignmentFormat, SupportedOption } from './alignmentconfig.js';
import type AlignmentCommand from './alignmentcommand.js';

const iconsMap = new Map( [
Expand Down Expand Up @@ -65,18 +74,78 @@ export default class AlignmentUI extends Plugin {
*/
public init(): void {
const editor = this.editor;
const componentFactory = editor.ui.componentFactory;
const t = editor.t;
const options = normalizeAlignmentOptions( editor.config.get( 'alignment.options' )! );

options
.map( option => option.name )
.filter( isSupported )
.forEach( option => this._addButton( option ) );

componentFactory.add( 'alignment', locale => {
this._addToolbarDropdown( options );
this._addMenuBarMenu( options );
}

/**
* Helper method for initializing the button and linking it with an appropriate command.
*
* @param option The name of the alignment option for which the button is added.
*/
private _addButton( option: SupportedOption ): void {
const editor = this.editor;

editor.ui.componentFactory.add( `alignment:${ option }`, locale => this._createButton( locale, option ) );
}

/**
* Helper method for creating the button view element.
*
* @param locale Editor locale.
* @param option The name of the alignment option for which the button is added.
* @param buttonAttrs Optional parameters passed to button view instance.
*/
private _createButton(
locale: Locale,
option: SupportedOption,
buttonAttrs: Partial<Button> = {}
): ButtonView {
const editor = this.editor;
const command: AlignmentCommand = editor.commands.get( 'alignment' )!;
const buttonView = new ButtonView( locale );

buttonView.set( {
label: this.localizedOptionTitles[ option ],
icon: iconsMap.get( option ),
tooltip: true,
isToggleable: true,
...buttonAttrs
} );

// Bind button model to command.
buttonView.bind( 'isEnabled' ).to( command );
buttonView.bind( 'isOn' ).to( command, 'value', value => value === option );

// Execute command.
this.listenTo( buttonView, 'execute', () => {
editor.execute( 'alignment', { value: option } );
editor.editing.view.focus();
} );

return buttonView;
}

/**
* Helper method for initializing the toolnar dropdown and linking it with an appropriate command.
*
* @param option The name of the alignment option for which the button is added.
*/
private _addToolbarDropdown( options: Array<AlignmentFormat> ): void {
const editor = this.editor;
const factory = editor.ui.componentFactory;

factory.add( 'alignment', locale => {
const dropdownView = createDropdown( locale );
const tooltipPosition = locale.uiLanguageDirection === 'rtl' ? 'w' : 'e';
const t = locale.t;

// Add existing alignment buttons to dropdown's toolbar.
addToolbarToDropdown(
Expand Down Expand Up @@ -122,50 +191,62 @@ export default class AlignmentUI extends Plugin {
}

/**
* Helper method for initializing the button and linking it with an appropriate command.
* Creates a menu for all alignment options to use either in menu bar.
*
* @param option The name of the alignment option for which the button is added.
* @param options Normalized alignment options from config.
*/
private _addButton( option: SupportedOption ): void {
private _addMenuBarMenu( options: Array<AlignmentFormat> ): void {
const editor = this.editor;

editor.ui.componentFactory.add( `alignment:${ option }`, locale => this._createButton( locale, option ) );
}
editor.ui.componentFactory.add( 'menuBar:alignment', locale => {
const command: AlignmentCommand = editor.commands.get( 'alignment' )!;
const t = locale.t;
const menuView = new MenuBarMenuView( locale );
const listView = new MenuBarMenuListView( locale );

/**
* Helper method for creating the button view element.
*
* @param locale Editor locale.
* @param option The name of the alignment option for which the button is added.
* @param buttonAttrs Optional parameters passed to button view instance.
*/
private _createButton(
locale: Locale,
option: SupportedOption,
buttonAttrs: Partial<Button> = {}
): ButtonView {
const editor = this.editor;
const command: AlignmentCommand = editor.commands.get( 'alignment' )!;
const buttonView = new ButtonView( locale );
menuView.bind( 'isEnabled' ).to( command );

buttonView.set( {
label: this.localizedOptionTitles[ option ],
icon: iconsMap.get( option ),
tooltip: true,
isToggleable: true,
...buttonAttrs
} );
listView.set( {
ariaLabel: t( 'Text alignment' ),
role: 'menu'
} );

// Bind button model to command.
buttonView.bind( 'isEnabled' ).to( command );
buttonView.bind( 'isOn' ).to( command, 'value', value => value === option );
menuView.buttonView.set( {
label: t( 'Text alignment' )
} );

// Execute command.
this.listenTo( buttonView, 'execute', () => {
editor.execute( 'alignment', { value: option } );
editor.editing.view.focus();
} );
for ( const option of options ) {
const listItemView = new MenuBarMenuListItemView( locale, menuView );
const buttonView = new MenuBarMenuListItemButtonView( locale );

return buttonView;
buttonView.extendTemplate( {
attributes: {
'aria-checked': buttonView.bindTemplate.to( 'isOn' )
}
} );

buttonView.delegate( 'execute' ).to( menuView );
buttonView.set( {
label: this.localizedOptionTitles[ option.name ],
icon: iconsMap.get( option.name )
} );

buttonView.on( 'execute', () => {
editor.execute( 'alignment', { value: option.name } );

editor.editing.view.focus();
} );

buttonView.bind( 'isOn' ).to( command, 'value', value => value === option.name );
buttonView.bind( 'isEnabled' ).to( command, 'isEnabled' );

listItemView.children.add( buttonView );
listView.items.add( listItemView );
}

menuView.panelView.children.add( listView );

return menuView;
} );
}
}
Loading

0 comments on commit f072048

Please sign in to comment.