From 247a0b4db7ca3ce9f2931c9fce0f8b9f7e78f6e3 Mon Sep 17 00:00:00 2001 From: Aleksander Nowodzinski Date: Wed, 24 Nov 2021 13:03:50 +0100 Subject: [PATCH 01/26] Initial implementation of the list properties UI. --- packages/ckeditor5-list/package.json | 6 +- packages/ckeditor5-list/src/liststyleui.js | 32 ++- .../ckeditor5-list/src/ui/collapsibleview.js | 136 +++++++++++ .../ckeditor5-list/src/ui/inputnumberview.js | 227 ++++++++++++++++++ .../src/ui/listpropertiesview.js | 186 ++++++++++++++ .../src/ui/numberedlistpropertiesview.js | 148 ++++++++++++ .../tests/manual/list-properties.html | 25 ++ .../tests/manual/list-properties.js | 141 +++++++++++ .../tests/manual/list-properties.md | 3 + .../ckeditor5-list/tests/manual/list-style.md | 2 +- packages/ckeditor5-list/theme/collapsible.css | 46 ++++ packages/ckeditor5-list/theme/inputnumber.css | 70 ++++++ .../ckeditor5-list/theme/listproperties.css | 14 ++ packages/ckeditor5-list/theme/liststyles.css | 6 +- .../theme/numberedlistproperties.css | 51 ++++ .../theme/ckeditor5-list/liststyles.css | 53 ++-- packages/ckeditor5-ui/src/focuscycler.js | 2 +- 17 files changed, 1101 insertions(+), 47 deletions(-) create mode 100644 packages/ckeditor5-list/src/ui/collapsibleview.js create mode 100644 packages/ckeditor5-list/src/ui/inputnumberview.js create mode 100644 packages/ckeditor5-list/src/ui/listpropertiesview.js create mode 100644 packages/ckeditor5-list/src/ui/numberedlistpropertiesview.js create mode 100644 packages/ckeditor5-list/tests/manual/list-properties.html create mode 100644 packages/ckeditor5-list/tests/manual/list-properties.js create mode 100644 packages/ckeditor5-list/tests/manual/list-properties.md create mode 100644 packages/ckeditor5-list/theme/collapsible.css create mode 100644 packages/ckeditor5-list/theme/inputnumber.css create mode 100644 packages/ckeditor5-list/theme/listproperties.css create mode 100644 packages/ckeditor5-list/theme/numberedlistproperties.css diff --git a/packages/ckeditor5-list/package.json b/packages/ckeditor5-list/package.json index ff85d8a9e2f..598ff95819d 100644 --- a/packages/ckeditor5-list/package.json +++ b/packages/ckeditor5-list/package.json @@ -12,7 +12,9 @@ ], "main": "src/index.js", "dependencies": { - "ckeditor5": "^31.0.0" + "ckeditor5": "^31.0.0", + "@ckeditor/ckeditor5-theme-lark": "^31.0.0", + "@ckeditor/ckeditor5-ui": "^31.0.0" }, "devDependencies": { "@ckeditor/ckeditor5-basic-styles": "^31.0.0", @@ -32,9 +34,7 @@ "@ckeditor/ckeditor5-paragraph": "^31.0.0", "@ckeditor/ckeditor5-remove-format": "^31.0.0", "@ckeditor/ckeditor5-table": "^31.0.0", - "@ckeditor/ckeditor5-theme-lark": "^31.0.0", "@ckeditor/ckeditor5-typing": "^31.0.0", - "@ckeditor/ckeditor5-ui": "^31.0.0", "@ckeditor/ckeditor5-undo": "^31.0.0", "@ckeditor/ckeditor5-utils": "^31.0.0", "webpack": "^4.43.0", diff --git a/packages/ckeditor5-list/src/liststyleui.js b/packages/ckeditor5-list/src/liststyleui.js index 981dfcedadf..00cfa29d085 100644 --- a/packages/ckeditor5-list/src/liststyleui.js +++ b/packages/ckeditor5-list/src/liststyleui.js @@ -8,7 +8,9 @@ */ import { Plugin } from 'ckeditor5/src/core'; -import { ButtonView, SplitButtonView, createDropdown, addToolbarToDropdown } from 'ckeditor5/src/ui'; +import { ButtonView, SplitButtonView, createDropdown } from 'ckeditor5/src/ui'; + +import ListPropertiesView from './ui/listpropertiesview'; import bulletedListIcon from '../theme/icons/bulletedlist.svg'; import numberedListIcon from '../theme/icons/numberedlist.svg'; @@ -51,7 +53,7 @@ export default class ListStyleUI extends Plugin { parentCommandName: 'bulletedList', buttonLabel: t( 'Bulleted List' ), buttonIcon: bulletedListIcon, - toolbarAriaLabel: t( 'Bulleted list styles toolbar' ), + styleGridAriaLabel: t( 'Bulleted list styles toolbar' ), styleDefinitions: [ { label: t( 'Toggle the disc list style' ), @@ -79,7 +81,7 @@ export default class ListStyleUI extends Plugin { parentCommandName: 'numberedList', buttonLabel: t( 'Numbered List' ), buttonIcon: numberedListIcon, - toolbarAriaLabel: t( 'Numbered list styles toolbar' ), + styleGridAriaLabel: t( 'Numbered list styles toolbar' ), styleDefinitions: [ { label: t( 'Toggle the decimal list style' ), @@ -131,10 +133,10 @@ export default class ListStyleUI extends Plugin { // the set of particular list styles (e.g. "bulletedList" for "disc", "circle", and "square" styles). // @param {String} options.buttonLabel Label of the main part of the split button. // @param {String} options.buttonIcon The SVG string of an icon for the main part of the split button. -// @param {String} options.toolbarAriaLabel The ARIA label for the toolbar in the split button dropdown. +// @param {String} options.styleGridAriaLabel The ARIA label for the styles grid in the split button dropdown. // @param {Object} options.styleDefinitions Definitions of the style buttons. // @returns {Function} A function that can be passed straight into {@link module:ui/componentfactory~ComponentFactory#add}. -function getSplitButtonCreator( { editor, parentCommandName, buttonLabel, buttonIcon, toolbarAriaLabel, styleDefinitions } ) { +function getSplitButtonCreator( { editor, parentCommandName, buttonLabel, buttonIcon, styleGridAriaLabel, styleDefinitions } ) { const parentCommand = editor.commands.get( parentCommandName ); const listStyleCommand = editor.commands.get( 'listStyle' ); @@ -143,12 +145,14 @@ function getSplitButtonCreator( { editor, parentCommandName, buttonLabel, button return locale => { const dropdownView = createDropdown( locale, SplitButtonView ); const splitButtonView = dropdownView.buttonView; + const enabledProperties = Object.fromEntries( editor.config.get( 'list.properties' ).map( key => [ key, true ] ) ); + const isNumberedListDropdown = parentCommandName == 'numberedList'; + const shouldIncludeStyles = isNumberedListDropdown ? !!enabledProperties.styles : true; + const shouldRenderNumberedListProperties = isNumberedListDropdown && ( enabledProperties.startIndex || enabledProperties.reversed ); const styleButtonCreator = getStyleButtonCreator( { editor, parentCommandName, listStyleCommand } ); - - addToolbarToDropdown( dropdownView, styleDefinitions.map( styleButtonCreator ) ); + const styleButtonViews = shouldIncludeStyles ? styleDefinitions.map( styleButtonCreator ) : null; dropdownView.bind( 'isEnabled' ).to( parentCommand ); - dropdownView.toolbarView.ariaLabel = toolbarAriaLabel; dropdownView.class = 'ck-list-styles-dropdown'; splitButtonView.on( 'execute', () => { @@ -165,6 +169,18 @@ function getSplitButtonCreator( { editor, parentCommandName, buttonLabel, button splitButtonView.bind( 'isOn' ).to( parentCommand, 'value', value => !!value ); + const listPropertiesView = new ListPropertiesView( locale, { + styleGridAriaLabel, + enabledProperties, + styleButtonViews, + shouldRenderNumberedListProperties + } ); + + // Make sure applying styles closes the dropdown. + listPropertiesView.delegate( 'execute' ).to( dropdownView ); + + dropdownView.panelView.children.add( listPropertiesView ); + return dropdownView; }; } diff --git a/packages/ckeditor5-list/src/ui/collapsibleview.js b/packages/ckeditor5-list/src/ui/collapsibleview.js new file mode 100644 index 00000000000..af7bdc6001c --- /dev/null +++ b/packages/ckeditor5-list/src/ui/collapsibleview.js @@ -0,0 +1,136 @@ +/** + * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +/** + * @module TODO + */ + +import { View, ButtonView } from 'ckeditor5/src/ui'; +import { FocusTracker } from 'ckeditor5/src/utils'; + +// eslint-disable-next-line ckeditor5-rules/ckeditor-imports +import dropdownArrowIcon from '@ckeditor/ckeditor5-ui/theme/icons/dropdown-arrow.svg'; + +import '../../theme/collapsible.css'; + +/** + * TODO + * + * @extends module:ui/view~View + */ +export default class CollapsibleView extends View { + /** + * @inheritDoc + */ + constructor( locale, childViews ) { + super( locale ); + + const bind = this.bindTemplate; + + /** + * TODO + */ + this.set( 'isCollapsed', false ); + + /** + * TODO + */ + this.set( 'label' ); + + /** + * TODO + */ + this.buttonView = this._createButtonView(); + + /** + * A collection of the child views. + * + * @readonly + * @member {module:ui/viewcollection~ViewCollection} + */ + this.children = this.createCollection(); + + /** + * Tracks information about the DOM focus in the collapsible. + * + * @readonly + * @protected + * @member {module:utils/focustracker~FocusTracker} + */ + this.focusTracker = new FocusTracker(); + + /** + * TODO + */ + this.set( '_collapsibleAriaLabelUid' ); + + if ( childViews ) { + this.children.addMany( childViews ); + } + + this.setTemplate( { + tag: 'div', + attributes: { + class: [ + 'ck', + 'ck-collapsible', + bind.if( 'isCollapsed', 'ck-collapsible_collapsed' ) + ] + }, + children: [ + this.buttonView, + { + tag: 'div', + attributes: { + class: [ + 'ck', + 'ck-collapsible__children' + ], + role: 'region', + hidden: bind.if( 'isCollapsed', 'hidden' ), + 'aria-labelledby': bind.to( '_collapsibleAriaLabelUid' ) + }, + children: this.children + } + ] + } ); + } + + render() { + super.render(); + + this._collapsibleAriaLabelUid = this.buttonView.labelView.element.id; + } + + /** + * TODO + * + * @returns + */ + _createButtonView() { + const buttonView = new ButtonView( this.locale ); + const bind = buttonView.bindTemplate; + + buttonView.set( { + withText: true, + icon: dropdownArrowIcon + } ); + + buttonView.extendTemplate( { + attributes: { + 'aria-expanded': bind.to( 'isOn', value => String( value ) ) + } + } ); + + buttonView.bind( 'label' ).to( this ); + buttonView.bind( 'isOn' ).to( this, 'isCollapsed', isCollapsed => !isCollapsed ); + + buttonView.on( 'execute', () => { + this.isCollapsed = !this.isCollapsed; + } ); + + return buttonView; + } +} diff --git a/packages/ckeditor5-list/src/ui/inputnumberview.js b/packages/ckeditor5-list/src/ui/inputnumberview.js new file mode 100644 index 00000000000..0de1165c9c4 --- /dev/null +++ b/packages/ckeditor5-list/src/ui/inputnumberview.js @@ -0,0 +1,227 @@ +/** + * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license + */ + +/** + * @module TODO + */ + +import { View } from 'ckeditor5/src/ui'; +import { FocusTracker } from 'ckeditor5/src/utils'; + +import '../../theme/inputnumber.css'; + +/** + * The number input view class. + * + * @extends module:ui/view~View + */ +export default class InputNumberView extends View { + /** + * @inheritDoc + */ + constructor( locale, { min, max, step } = {} ) { + super( locale ); + + /** + * The value of the input. + * + * @observable + * @member {String} #value + */ + this.set( 'value' ); + + /** + * The `id` attribute of the input (i.e. to pair with a `