From 3e907b7e5b93feb674ee6c429d2126f126c263dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Remiszewski?= Date: Wed, 28 Feb 2024 15:06:52 +0100 Subject: [PATCH 1/2] Add list integration. --- packages/ckeditor5-list/src/list/listui.ts | 7 +- packages/ckeditor5-list/src/list/utils.ts | 54 ++-- .../src/listproperties/listpropertiesui.ts | 239 +++++++++++++----- .../ckeditor5-list/src/todolist/todolistui.ts | 4 +- .../ckeditor5-ui/src/menubar/menubarview.ts | 4 + 5 files changed, 222 insertions(+), 86 deletions(-) diff --git a/packages/ckeditor5-list/src/list/listui.ts b/packages/ckeditor5-list/src/list/listui.ts index 0a384021728..c253e0c3ad4 100644 --- a/packages/ckeditor5-list/src/list/listui.ts +++ b/packages/ckeditor5-list/src/list/listui.ts @@ -7,9 +7,10 @@ * @module list/list/listui */ -import { createUIComponent } from './utils.js'; +import { createUIComponents } from './utils.js'; import { icons, Plugin } from 'ckeditor5/src/core.js'; +import { MenuBarMenuListItemButtonView } from 'ckeditor5/src/ui.js'; /** * The list UI feature. It introduces the `'numberedList'` and `'bulletedList'` buttons that @@ -30,7 +31,7 @@ export default class ListUI extends Plugin { const t = this.editor.t; // Create two buttons and link them with numberedList and bulletedList commands. - createUIComponent( this.editor, 'numberedList', t( 'Numbered List' ), icons.numberedList ); - createUIComponent( this.editor, 'bulletedList', t( 'Bulleted List' ), icons.bulletedList ); + createUIComponents( this.editor, 'numberedList', t( 'Numbered List' ), icons.numberedList ); + createUIComponents( this.editor, 'bulletedList', t( 'Bulleted List' ), icons.bulletedList ); } } diff --git a/packages/ckeditor5-list/src/list/utils.ts b/packages/ckeditor5-list/src/list/utils.ts index 7502a194817..beb80932506 100644 --- a/packages/ckeditor5-list/src/list/utils.ts +++ b/packages/ckeditor5-list/src/list/utils.ts @@ -8,10 +8,10 @@ */ import type { Editor } from 'ckeditor5/src/core.js'; -import { ButtonView, type ButtonExecuteEvent } from 'ckeditor5/src/ui.js'; +import { ButtonView, MenuBarMenuListItemButtonView, type ButtonExecuteEvent } from 'ckeditor5/src/ui.js'; /** - * Helper method for creating a UI button and linking it with an appropriate command. + * Helper method for creating toolbar and menu buttons and linking them with an appropriate command. * * @internal * @param editor The editor instance to which the UI component will be added. @@ -19,32 +19,54 @@ import { ButtonView, type ButtonExecuteEvent } from 'ckeditor5/src/ui.js'; * @param label The button label. * @param icon The source of the icon. */ -export function createUIComponent( +export function createUIComponents( editor: Editor, commandName: 'bulletedList' | 'numberedList' | 'todoList', label: string, icon: string ): void { - editor.ui.componentFactory.add( commandName, locale => { - const command = editor.commands.get( commandName )!; - const buttonView = new ButtonView( locale ); + editor.ui.componentFactory.add( commandName, () => { + const buttonView = _createButton( ButtonView, editor, commandName, label, icon ); buttonView.set( { - label, - icon, tooltip: true, isToggleable: true } ); - // Bind button model to command. - buttonView.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' ); + return buttonView; + } ); - // Execute command. - buttonView.on( 'execute', () => { - editor.execute( commandName ); - editor.editing.view.focus(); - } ); + editor.ui.componentFactory.add( `menuBar:${ commandName }`, () => + _createButton( MenuBarMenuListItemButtonView, editor, commandName, label, icon ) + ); +} - return buttonView; +/** + * Creates a button to use either in toolbar or in menu bar. + */ +function _createButton( + ButtonClass: T, + editor: Editor, + commandName: 'bulletedList' | 'numberedList' | 'todoList', + label: string, + icon: string +): InstanceType { + const command = editor.commands.get( commandName )!; + const view = new ButtonClass( editor.locale ) as InstanceType; + + view.set( { + label, + icon } ); + + // Bind button model to command. + view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' ); + + // Execute the command. + view.on( 'execute', () => { + editor.execute( commandName ); + editor.editing.view.focus(); + } ); + + return view; } diff --git a/packages/ckeditor5-list/src/listproperties/listpropertiesui.ts b/packages/ckeditor5-list/src/listproperties/listpropertiesui.ts index 2e5effee715..c8c5c9c867e 100644 --- a/packages/ckeditor5-list/src/listproperties/listpropertiesui.ts +++ b/packages/ckeditor5-list/src/listproperties/listpropertiesui.ts @@ -14,7 +14,11 @@ import { SplitButtonView, createDropdown, focusChildOnDropdownOpen, - type DropdownView + type DropdownView, + MenuBarMenuView, + MenuBarMenuListView, + MenuBarMenuListItemView, + MenuBarMenuListItemButtonView } from 'ckeditor5/src/ui.js'; import type { Locale } from 'ckeditor5/src/utils.js'; @@ -64,84 +68,98 @@ export default class ListPropertiesUI extends Plugin { // a simple button will be still registered under the same name by ListUI as a fallback. This should happen // in most editor configuration because the List plugin automatically requires ListUI. if ( enabledProperties.styles ) { - editor.ui.componentFactory.add( 'bulletedList', getDropdownViewCreator( { + const styleDefinitions = [ + { + label: t( 'Toggle the disc list style' ), + tooltip: t( 'Disc' ), + type: 'disc', + icon: listStyleDiscIcon + }, + { + label: t( 'Toggle the circle list style' ), + tooltip: t( 'Circle' ), + type: 'circle', + icon: listStyleCircleIcon + }, + { + label: t( 'Toggle the square list style' ), + tooltip: t( 'Square' ), + type: 'square', + icon: listStyleSquareIcon + } + ]; + const buttonLabel = t( 'Bulleted list' ); + const commandName = 'bulletedList'; + + editor.ui.componentFactory.add( commandName, getDropdownViewCreator( { editor, - parentCommandName: 'bulletedList', - buttonLabel: t( 'Bulleted List' ), + parentCommandName: commandName, + buttonLabel, buttonIcon: icons.bulletedList, styleGridAriaLabel: t( 'Bulleted list styles toolbar' ), - styleDefinitions: [ - { - label: t( 'Toggle the disc list style' ), - tooltip: t( 'Disc' ), - type: 'disc', - icon: listStyleDiscIcon - }, - { - label: t( 'Toggle the circle list style' ), - tooltip: t( 'Circle' ), - type: 'circle', - icon: listStyleCircleIcon - }, - { - label: t( 'Toggle the square list style' ), - tooltip: t( 'Square' ), - type: 'square', - icon: listStyleSquareIcon - } - ] + styleDefinitions } ) ); + + // Add menu item for bulleted list. + createMenuItem( editor, commandName, buttonLabel, t( 'Bulleted' ), styleDefinitions ); } // Note: When this plugin does not register the "numberedList" dropdown due to properties configuration, // a simple button will be still registered under the same name by ListUI as a fallback. This should happen // in most editor configuration because the List plugin automatically requires ListUI. if ( enabledProperties.styles || enabledProperties.startIndex || enabledProperties.reversed ) { - editor.ui.componentFactory.add( 'numberedList', getDropdownViewCreator( { + const styleDefinitions = [ + { + label: t( 'Toggle the decimal list style' ), + tooltip: t( 'Decimal' ), + type: 'decimal', + icon: listStyleDecimalIcon + }, + { + label: t( 'Toggle the decimal with leading zero list style' ), + tooltip: t( 'Decimal with leading zero' ), + type: 'decimal-leading-zero', + icon: listStyleDecimalWithLeadingZeroIcon + }, + { + label: t( 'Toggle the lower–roman list style' ), + tooltip: t( 'Lower–roman' ), + type: 'lower-roman', + icon: listStyleLowerRomanIcon + }, + { + label: t( 'Toggle the upper–roman list style' ), + tooltip: t( 'Upper-roman' ), + type: 'upper-roman', + icon: listStyleUpperRomanIcon + }, + { + label: t( 'Toggle the lower–latin list style' ), + tooltip: t( 'Lower-latin' ), + type: 'lower-latin', + icon: listStyleLowerLatinIcon + }, + { + label: t( 'Toggle the upper–latin list style' ), + tooltip: t( 'Upper-latin' ), + type: 'upper-latin', + icon: listStyleUpperLatinIcon + } + ]; + const buttonLabel = t( 'Numbered list' ); + const commandName = 'numberedList'; + + editor.ui.componentFactory.add( commandName, getDropdownViewCreator( { editor, - parentCommandName: 'numberedList', - buttonLabel: t( 'Numbered List' ), + parentCommandName: commandName, + buttonLabel, buttonIcon: icons.numberedList, styleGridAriaLabel: t( 'Numbered list styles toolbar' ), - styleDefinitions: [ - { - label: t( 'Toggle the decimal list style' ), - tooltip: t( 'Decimal' ), - type: 'decimal', - icon: listStyleDecimalIcon - }, - { - label: t( 'Toggle the decimal with leading zero list style' ), - tooltip: t( 'Decimal with leading zero' ), - type: 'decimal-leading-zero', - icon: listStyleDecimalWithLeadingZeroIcon - }, - { - label: t( 'Toggle the lower–roman list style' ), - tooltip: t( 'Lower–roman' ), - type: 'lower-roman', - icon: listStyleLowerRomanIcon - }, - { - label: t( 'Toggle the upper–roman list style' ), - tooltip: t( 'Upper-roman' ), - type: 'upper-roman', - icon: listStyleUpperRomanIcon - }, - { - label: t( 'Toggle the lower–latin list style' ), - tooltip: t( 'Lower-latin' ), - type: 'lower-latin', - icon: listStyleLowerLatinIcon - }, - { - label: t( 'Toggle the upper–latin list style' ), - tooltip: t( 'Upper-latin' ), - type: 'upper-latin', - icon: listStyleUpperLatinIcon - } - ] + styleDefinitions } ) ); + + // Add menu item for numbered list. + createMenuItem( editor, commandName, buttonLabel, t( 'Numbered' ), styleDefinitions ); } } } @@ -364,6 +382,97 @@ function createListPropertiesView( { return listPropertiesView; } +/** + * A helper that creates the list style submenu for menu bar. + * + * @param editor Editor instance. + * @param commandName Name of the list command. + * @param mainItemLabel Short list type name. + * @param defaultStyleLabel Label for `default` list style. + * @param styleDefinitions Array of avaialble styles for processed list type. + */ +function createMenuItem( + editor: Editor, + commandName: 'bulletedList' | 'numberedList', + mainItemLabel: string, + defaultStyleLabel: string, + styleDefinitions: Array +) { + editor.ui.componentFactory.add( `menuBar:${ commandName }`, locale => { + const menuView = new MenuBarMenuView( locale ); + const listCommand = editor.commands.get( commandName )!; + const listStyleCommand = editor.commands.get( 'listStyle' )!; + const listView = new MenuBarMenuListView( locale ); + + menuView.buttonView.set( { + label: mainItemLabel, + icon: icons[ commandName ] + } ); + + menuView.panelView.children.add( listView ); + + const options = [ + { + tooltip: defaultStyleLabel, + type: 'default', + icon: icons[ commandName ] + }, + ...styleDefinitions + ]; + + for ( const option of options ) { + const listItemView = new MenuBarMenuListItemView( locale, menuView ); + const buttonView = new MenuBarMenuListItemButtonView( locale ); + + listItemView.children.add( buttonView ); + listView.items.add( listItemView ); + + buttonView.set( { + label: option.tooltip, + // role: 'menuitemradio', + icon: option.icon + } ); + + buttonView.delegate( 'execute' ).to( menuView ); + + // Keep current list option highlighted. + listStyleCommand.on( 'change:value', () => { + buttonView.isOn = listStyleCommand.value === option.type && + listCommand.value === true; + } ); + listCommand.on( 'change:value', () => { + buttonView.isOn = listStyleCommand.value === option.type && + listCommand.value === true; + } ); + + buttonView.on( 'execute', () => { + // If current list style is selected, execute main list command to remove list format. + if ( listStyleCommand.value == option.type ) { + editor.execute( commandName ); + } + // For unique styles, just call `listStyle` command. + else if ( option.type != 'default' ) { + editor.execute( 'listStyle', { type: option.type } ); + } + // For 'default' style we need to call main list command if slected element is not a list. + else if ( !listCommand.value ) { + editor.execute( commandName ); + } + // Or change list style to 'default' if selected element is a list of custom style. + else { + editor.execute( 'listStyle', { type: option.type } ); + } + + editor.editing.view.focus(); + } ); + } + + menuView.bind( 'isEnabled' ).to( listCommand, 'isEnabled' ); + + return menuView; + } ); +} + interface StyleDefinition { /** diff --git a/packages/ckeditor5-list/src/todolist/todolistui.ts b/packages/ckeditor5-list/src/todolist/todolistui.ts index 3a488ca4780..5764ebe8a25 100644 --- a/packages/ckeditor5-list/src/todolist/todolistui.ts +++ b/packages/ckeditor5-list/src/todolist/todolistui.ts @@ -7,7 +7,7 @@ * @module list/todolist/todolistui */ -import { createUIComponent } from '../list/utils.js'; +import { createUIComponents } from '../list/utils.js'; import { icons, Plugin } from 'ckeditor5/src/core.js'; /** @@ -28,6 +28,6 @@ export default class TodoListUI extends Plugin { public init(): void { const t = this.editor.t; - createUIComponent( this.editor, 'todoList', t( 'To-do List' ), icons.todoList ); + createUIComponents( this.editor, 'todoList', t( 'To-do List' ), icons.todoList ); } } diff --git a/packages/ckeditor5-ui/src/menubar/menubarview.ts b/packages/ckeditor5-ui/src/menubar/menubarview.ts index a36d7b18ccc..2db5a40bca4 100644 --- a/packages/ckeditor5-ui/src/menubar/menubarview.ts +++ b/packages/ckeditor5-ui/src/menubar/menubarview.ts @@ -162,6 +162,10 @@ export default class MenuBarView extends View implements FocusableView { 'menuBar:italic', 'menuBar:underline', '-', + 'menuBar:bulletedList', + 'menuBar:numberedList', + 'menuBar:todoList', + '-', 'menuBar:heading' ] }, From 04674e8fb37a8b7dbdba01d4d3f6b35d7744df33 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Fri, 1 Mar 2024 12:49:03 +0100 Subject: [PATCH 2/2] Used list style grid for list properties in the menu bar. Secured the menu bar menu against list styles being disallowed in the list properties configuration. --- packages/ckeditor5-list/src/list/listui.ts | 2 - .../src/listproperties/listpropertiesui.ts | 204 ++++++++++-------- .../tests/listproperties/listpropertiesui.js | 4 +- 3 files changed, 111 insertions(+), 99 deletions(-) diff --git a/packages/ckeditor5-list/src/list/listui.ts b/packages/ckeditor5-list/src/list/listui.ts index c253e0c3ad4..fa4c43d0020 100644 --- a/packages/ckeditor5-list/src/list/listui.ts +++ b/packages/ckeditor5-list/src/list/listui.ts @@ -8,9 +8,7 @@ */ import { createUIComponents } from './utils.js'; - import { icons, Plugin } from 'ckeditor5/src/core.js'; -import { MenuBarMenuListItemButtonView } from 'ckeditor5/src/ui.js'; /** * The list UI feature. It introduces the `'numberedList'` and `'bulletedList'` buttons that diff --git a/packages/ckeditor5-list/src/listproperties/listpropertiesui.ts b/packages/ckeditor5-list/src/listproperties/listpropertiesui.ts index c8c5c9c867e..cd7da579571 100644 --- a/packages/ckeditor5-list/src/listproperties/listpropertiesui.ts +++ b/packages/ckeditor5-list/src/listproperties/listpropertiesui.ts @@ -14,11 +14,8 @@ import { SplitButtonView, createDropdown, focusChildOnDropdownOpen, - type DropdownView, MenuBarMenuView, - MenuBarMenuListView, - MenuBarMenuListItemView, - MenuBarMenuListItemButtonView + type DropdownView } from 'ckeditor5/src/ui.js'; import type { Locale } from 'ckeditor5/src/utils.js'; @@ -43,6 +40,7 @@ import listStyleLowerLatinIcon from '../../theme/icons/liststylelowerlatin.svg'; import listStyleUpperLatinIcon from '../../theme/icons/liststyleupperlatin.svg'; import '../../theme/liststyles.css'; +import type { ListPropertiesConfig } from '../listconfig.js'; /** * The list properties UI plugin. It introduces the extended `'bulletedList'` and `'numberedList'` toolbar @@ -62,12 +60,12 @@ export default class ListPropertiesUI extends Plugin { public init(): void { const editor = this.editor; const t = editor.locale.t; - const enabledProperties = editor.config.get( 'list.properties' )!; + const propertiesConfig = editor.config.get( 'list.properties' )!; // Note: When this plugin does not register the "bulletedList" dropdown due to properties configuration, // a simple button will be still registered under the same name by ListUI as a fallback. This should happen // in most editor configuration because the List plugin automatically requires ListUI. - if ( enabledProperties.styles ) { + if ( propertiesConfig.styles ) { const styleDefinitions = [ { label: t( 'Toggle the disc list style' ), @@ -88,26 +86,35 @@ export default class ListPropertiesUI extends Plugin { icon: listStyleSquareIcon } ]; - const buttonLabel = t( 'Bulleted list' ); + const buttonLabel = t( 'Bulleted List' ); + const styleGridAriaLabel = t( 'Bulleted list styles toolbar' ); const commandName = 'bulletedList'; editor.ui.componentFactory.add( commandName, getDropdownViewCreator( { editor, + propertiesConfig, parentCommandName: commandName, buttonLabel, buttonIcon: icons.bulletedList, - styleGridAriaLabel: t( 'Bulleted list styles toolbar' ), + styleGridAriaLabel, styleDefinitions } ) ); - // Add menu item for bulleted list. - createMenuItem( editor, commandName, buttonLabel, t( 'Bulleted' ), styleDefinitions ); + // Add the menu bar item for bulleted list. + editor.ui.componentFactory.add( `menuBar:${ commandName }`, getMenuBarStylesMenuCreator( { + editor, + propertiesConfig, + parentCommandName: commandName, + buttonLabel, + styleGridAriaLabel, + styleDefinitions + } ) ); } // Note: When this plugin does not register the "numberedList" dropdown due to properties configuration, // a simple button will be still registered under the same name by ListUI as a fallback. This should happen // in most editor configuration because the List plugin automatically requires ListUI. - if ( enabledProperties.styles || enabledProperties.startIndex || enabledProperties.reversed ) { + if ( propertiesConfig.styles || propertiesConfig.startIndex || propertiesConfig.reversed ) { const styleDefinitions = [ { label: t( 'Toggle the decimal list style' ), @@ -146,20 +153,32 @@ export default class ListPropertiesUI extends Plugin { icon: listStyleUpperLatinIcon } ]; - const buttonLabel = t( 'Numbered list' ); + const buttonLabel = t( 'Numbered List' ); + const styleGridAriaLabel = t( 'Numbered list styles toolbar' ); const commandName = 'numberedList'; editor.ui.componentFactory.add( commandName, getDropdownViewCreator( { editor, + propertiesConfig, parentCommandName: commandName, buttonLabel, buttonIcon: icons.numberedList, - styleGridAriaLabel: t( 'Numbered list styles toolbar' ), + styleGridAriaLabel, styleDefinitions } ) ); - // Add menu item for numbered list. - createMenuItem( editor, commandName, buttonLabel, t( 'Numbered' ), styleDefinitions ); + // Menu bar menu does not display list start index or reverse UI. If there are no styles enabled, + // the menu makes no sense and should be omitted. + if ( propertiesConfig.styles ) { + editor.ui.componentFactory.add( `menuBar:${ commandName }`, getMenuBarStylesMenuCreator( { + editor, + propertiesConfig, + parentCommandName: commandName, + buttonLabel, + styleGridAriaLabel, + styleDefinitions + } ) ); + } } } } @@ -169,6 +188,7 @@ export default class ListPropertiesUI extends Plugin { * which in turn contains buttons allowing users to change list styles in the context of the current selection. * * @param options.editor + * @param options.propertiesConfig List properties configuration. * @param options.parentCommandName The name of the higher-order editor command associated with * the set of particular list styles (e.g. "bulletedList" for "disc", "circle", and "square" styles). * @param options.buttonLabel Label of the main part of the split button. @@ -179,6 +199,7 @@ export default class ListPropertiesUI extends Plugin { */ function getDropdownViewCreator( { editor, + propertiesConfig, parentCommandName, buttonLabel, buttonIcon, @@ -186,6 +207,7 @@ function getDropdownViewCreator( { styleDefinitions }: { editor: Editor; + propertiesConfig: Readonly; parentCommandName: string; buttonLabel: string; buttonIcon: string; @@ -219,6 +241,7 @@ function getDropdownViewCreator( { dropdownView.once( 'change:isOpen', () => { const listPropertiesView = createListPropertiesView( { editor, + propertiesConfig, dropdownView, parentCommandName, styleGridAriaLabel, @@ -272,9 +295,13 @@ function getStyleButtonCreator( { button.on( 'execute', () => { // If the content the selection is anchored to is a list, let's change its style. if ( parentCommand.value ) { + // Remove the list when the current list style is the same as the one that would normally be applied. + if ( listStyleCommand.value === type ) { + editor.execute( parentCommandName ); + } // If the current list style is not set in the model or the style is different than the // one to be applied, simply apply the new style. - if ( listStyleCommand.value !== type ) { + else if ( listStyleCommand.value !== type ) { editor.execute( 'listStyle', { type } ); } // If the style was the same, remove it (the button works as an off toggle). @@ -298,6 +325,7 @@ function getStyleButtonCreator( { * A helper that creates the properties view for the individual style dropdown. * * @param options.editor Editor instance. + * @param options.propertiesConfig List properties configuration. * @param options.dropdownView Styles dropdown view that hosts the properties view. * @param options.parentCommandName The name of the higher-order editor command associated with * the set of particular list styles (e.g. "bulletedList" for "disc", "circle", and "square" styles). @@ -306,26 +334,31 @@ function getStyleButtonCreator( { */ function createListPropertiesView( { editor, + propertiesConfig, dropdownView, parentCommandName, styleDefinitions, styleGridAriaLabel }: { editor: Editor; + propertiesConfig: Readonly; dropdownView: DropdownView; parentCommandName: string; styleDefinitions: Array; styleGridAriaLabel: string; } ) { const locale = editor.locale; - const enabledProperties = editor.config.get( 'list.properties' )!; - let styleButtonViews = null; + const enabledProperties = { + ...propertiesConfig + }; if ( parentCommandName != 'numberedList' ) { enabledProperties.startIndex = false; enabledProperties.reversed = false; } + let styleButtonViews = null; + if ( enabledProperties.styles ) { const listStyleCommand: LegacyListStyleCommand | ListStyleCommand = editor.commands.get( 'listStyle' )!; @@ -336,9 +369,7 @@ function createListPropertiesView( { } ); // The command can be ListStyleCommand or DocumentListStyleCommand. - const isStyleTypeSupported = typeof listStyleCommand.isStyleTypeSupported == 'function' ? - ( styleDefinition: StyleDefinition ) => listStyleCommand.isStyleTypeSupported( styleDefinition.type ) : - () => true; + const isStyleTypeSupported = getStyleTypeSupportChecker( listStyleCommand ); styleButtonViews = styleDefinitions.filter( isStyleTypeSupported ).map( styleButtonCreator ); } @@ -386,91 +417,74 @@ function createListPropertiesView( { * A helper that creates the list style submenu for menu bar. * * @param editor Editor instance. - * @param commandName Name of the list command. - * @param mainItemLabel Short list type name. - * @param defaultStyleLabel Label for `default` list style. - * @param styleDefinitions Array of avaialble styles for processed list type. + * @param propertiesConfig List properties configuration. + * @param parentCommandName Name of the list command. + * @param buttonLabel Label of the menu button. + * @param styleGridAriaLabel ARIA label of the styles grid. + * @param styleDefinitions Array of available styles for processed list type. */ -function createMenuItem( - editor: Editor, - commandName: 'bulletedList' | 'numberedList', - mainItemLabel: string, - defaultStyleLabel: string, - styleDefinitions: Array +function getMenuBarStylesMenuCreator( + { + editor, + propertiesConfig, + parentCommandName, + buttonLabel, + styleGridAriaLabel, + styleDefinitions + }: { + editor: Editor; + propertiesConfig: Readonly; + parentCommandName: 'bulletedList' | 'numberedList'; + buttonLabel: string; + styleGridAriaLabel: string; + styleDefinitions: Array; + } ) { - editor.ui.componentFactory.add( `menuBar:${ commandName }`, locale => { + return ( locale: Locale ) => { const menuView = new MenuBarMenuView( locale ); - const listCommand = editor.commands.get( commandName )!; + const listCommand = editor.commands.get( parentCommandName )!; const listStyleCommand = editor.commands.get( 'listStyle' )!; - const listView = new MenuBarMenuListView( locale ); - - menuView.buttonView.set( { - label: mainItemLabel, - icon: icons[ commandName ] + const isStyleTypeSupported = getStyleTypeSupportChecker( listStyleCommand ); + const styleButtonCreator = getStyleButtonCreator( { + editor, + parentCommandName, + listStyleCommand } ); - - menuView.panelView.children.add( listView ); - - const options = [ - { - tooltip: defaultStyleLabel, - type: 'default', - icon: icons[ commandName ] + const styleButtonViews = styleDefinitions.filter( isStyleTypeSupported ).map( styleButtonCreator ); + const listPropertiesView = new ListPropertiesView( locale, { + styleGridAriaLabel, + enabledProperties: { + ...propertiesConfig, + + // Disable list start index and reversed in the menu bar. + startIndex: false, + reversed: false }, - ...styleDefinitions - ]; - - for ( const option of options ) { - const listItemView = new MenuBarMenuListItemView( locale, menuView ); - const buttonView = new MenuBarMenuListItemButtonView( locale ); - - listItemView.children.add( buttonView ); - listView.items.add( listItemView ); - - buttonView.set( { - label: option.tooltip, - // role: 'menuitemradio', - icon: option.icon - } ); - - buttonView.delegate( 'execute' ).to( menuView ); - - // Keep current list option highlighted. - listStyleCommand.on( 'change:value', () => { - buttonView.isOn = listStyleCommand.value === option.type && - listCommand.value === true; - } ); - listCommand.on( 'change:value', () => { - buttonView.isOn = listStyleCommand.value === option.type && - listCommand.value === true; - } ); - - buttonView.on( 'execute', () => { - // If current list style is selected, execute main list command to remove list format. - if ( listStyleCommand.value == option.type ) { - editor.execute( commandName ); - } - // For unique styles, just call `listStyle` command. - else if ( option.type != 'default' ) { - editor.execute( 'listStyle', { type: option.type } ); - } - // For 'default' style we need to call main list command if slected element is not a list. - else if ( !listCommand.value ) { - editor.execute( commandName ); - } - // Or change list style to 'default' if selected element is a list of custom style. - else { - editor.execute( 'listStyle', { type: option.type } ); - } + styleButtonViews + } ); - editor.editing.view.focus(); - } ); - } + listPropertiesView.delegate( 'execute' ).to( menuView ); + menuView.buttonView.set( { + label: buttonLabel, + icon: icons[ parentCommandName ] + } ); + menuView.panelView.children.add( listPropertiesView ); menuView.bind( 'isEnabled' ).to( listCommand, 'isEnabled' ); + menuView.on( 'execute', () => { + editor.editing.view.focus(); + } ); return menuView; - } ); + }; +} + +function getStyleTypeSupportChecker( listStyleCommand: LegacyListStyleCommand | ListStyleCommand ) { + if ( typeof listStyleCommand.isStyleTypeSupported == 'function' ) { + return ( styleDefinition: StyleDefinition ) => listStyleCommand.isStyleTypeSupported( styleDefinition.type ); + } else { + return () => true; + } } interface StyleDefinition { diff --git a/packages/ckeditor5-list/tests/listproperties/listpropertiesui.js b/packages/ckeditor5-list/tests/listproperties/listpropertiesui.js index 06bf61dd71b..fe0e7f51091 100644 --- a/packages/ckeditor5-list/tests/listproperties/listpropertiesui.js +++ b/packages/ckeditor5-list/tests/listproperties/listpropertiesui.js @@ -345,7 +345,7 @@ describe( 'ListPropertiesUI', () => { styleButtonView.fire( 'execute' ); - sinon.assert.calledWithExactly( editor.execute, 'listStyle', { type: 'default' } ); + sinon.assert.calledWithExactly( editor.execute, 'bulletedList' ); sinon.assert.calledOnce( editor.editing.view.focus ); sinon.assert.callOrder( editor.execute, editor.editing.view.focus ); } ); @@ -728,7 +728,7 @@ describe( 'ListPropertiesUI', () => { styleButtonView.fire( 'execute' ); - sinon.assert.calledWithExactly( editor.execute, 'listStyle', { type: 'default' } ); + sinon.assert.calledWithExactly( editor.execute, 'numberedList' ); sinon.assert.calledOnce( editor.editing.view.focus ); sinon.assert.callOrder( editor.execute, editor.editing.view.focus ); } );