Browse Source

Changed: Extract common dropdown tasks to helper methods.

Maciej Gołaszewski 8 years ago
parent
commit
e3301dd22c

+ 0 - 16
packages/ckeditor5-ui/src/dropdown/button/buttondropdownmodel.jsdoc

@@ -35,22 +35,6 @@
  * @member {Boolean} #isVertical=false
  */
 
-/**
- * Disables automatic button icon binding. If set to true dropdown's button {@link #icon} will be set to {@link #defaultIcon}.
- *
- * @observable
- * @member {Boolean} #staticIcon=false
- */
-
-/**
- * Defines default icon which is used when no button is active.
- *
- * Also see {@link #icon}.
- *
- * @observable
- * @member {String} #defaultIcon
- */
-
 /**
  * Button dropdown icon is set from inner button views.
  *

+ 6 - 70
packages/ckeditor5-ui/src/dropdown/button/createbuttondropdown.js

@@ -7,13 +7,10 @@
  * @module ui/dropdown/button/createbuttondropdown
  */
 
-import ToolbarView from '../../toolbar/toolbarview';
 import {
-	closeDropdownOnBlur,
-	closeDropdownOnExecute,
-	createButtonForDropdown,
-	createDropdownView,
-	focusDropdownContentsOnArrows
+	addDefaultBehavior,
+	addToolbarToDropdown,
+	createSingleButtonDropdown
 } from '../utils';
 
 import '../../../theme/components/dropdown/buttondropdown.css';
@@ -52,71 +49,10 @@ import '../../../theme/components/dropdown/buttondropdown.css';
  * @returns {module:ui/dropdown/dropdownview~DropdownView}
  */
 export default function createButtonDropdown( model, locale ) {
-	// Make disabled when all buttons are disabled
-	model.bind( 'isEnabled' ).to(
-		// Bind to #isEnabled of each command...
-		...getBindingTargets( model.buttons, 'isEnabled' ),
-		// ...and set it true if any command #isEnabled is true.
-		( ...areEnabled ) => areEnabled.some( isEnabled => isEnabled )
-	);
+	const dropdownView = createSingleButtonDropdown( model, locale );
 
-	// If defined `staticIcon` use the `defaultIcon` without binding it to active a button.
-	if ( model.staticIcon ) {
-		model.bind( 'icon' ).to( model, 'defaultIcon' );
-	} else {
-		// TODO: move to alignment
-		// Make dropdown icon as any active button.
-		model.bind( 'icon' ).to(
-			// Bind to #isOn of each button...
-			...getBindingTargets( model.buttons, 'isOn' ),
-			// ...and chose the title of the first one which #isOn is true.
-			( ...areActive ) => {
-				const index = areActive.findIndex( value => value );
-
-				// If none of the commands is active, display either defaultIcon or first button icon.
-				if ( index < 0 && model.defaultIcon ) {
-					return model.defaultIcon;
-				}
-
-				return model.buttons[ index < 0 ? 0 : index ].icon;
-			}
-		);
-	}
-	const buttonView = createButtonForDropdown( model, locale );
-	const dropdownView = createDropdownView( model, buttonView, locale );
-
-	const toolbarView = dropdownView.toolbarView = new ToolbarView();
-
-	toolbarView.bind( 'isVertical', 'className' ).to( model, 'isVertical', 'toolbarClassName' );
-
-	model.buttons.map( view => toolbarView.items.add( view ) );
-
-	// TODO: better:
-	dropdownView.buttonView.delegate( 'execute' ).to( dropdownView.buttonView, 'select' );
-
-	dropdownView.extendTemplate( {
-		attributes: {
-			class: [ 'ck-buttondropdown' ]
-		}
-	} );
-
-	dropdownView.panelView.children.add( toolbarView );
-
-	closeDropdownOnBlur( dropdownView );
-	closeDropdownOnExecute( dropdownView, toolbarView.items );
-	focusDropdownContentsOnArrows( dropdownView, toolbarView );
+	addToolbarToDropdown( dropdownView, model );
+	addDefaultBehavior( dropdownView );
 
 	return dropdownView;
 }
-
-// Returns an array of binding components for
-// {@link module:utils/observablemixin~Observable#bind} from a set of iterable
-// buttons.
-//
-// @private
-// @param {Iterable.<module:ui/button/buttonview~ButtonView>} buttons
-// @param {String} attribute
-// @returns {Array.<String>}
-function getBindingTargets( buttons, attribute ) {
-	return Array.prototype.concat( ...buttons.map( button => [ button, attribute ] ) );
-}

+ 0 - 34
packages/ckeditor5-ui/src/dropdown/createsplitbuttondropdown.js

@@ -1,34 +0,0 @@
-/**
- * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module ui/dropdown/createsplitbuttondropdown
- */
-
-import { createDropdownView, createSplitButtonForDropdown } from './utils';
-
-/**
- * Create a dropdown that have a split button as button.
- *
- * TODO: docs
- */
-export default function createSplitButtonDropdown( model, locale ) {
-	const splitButtonView = createSplitButtonForDropdown( model, locale );
-
-	splitButtonView.buttonView.bind( 'isOn' ).to( model );
-	splitButtonView.buttonView.bind( 'tooltip' ).to( model );
-
-	const dropdownView = createDropdownView( model, splitButtonView, locale );
-
-	// Extend template to hide arrow from dropdown.
-	// TODO: enable this on normal button instead of hiding it
-	dropdownView.extendTemplate( {
-		attributes: {
-			class: 'ck-splitbutton-dropdown'
-		}
-	} );
-
-	return dropdownView;
-}

+ 14 - 0
packages/ckeditor5-ui/src/dropdown/dropdownpanelview.js

@@ -65,4 +65,18 @@ export default class DropdownPanelView extends View {
 			}
 		} );
 	}
+
+	focus() {
+		if ( this.children.length ) {
+			this.children.first.focus();
+		}
+	}
+
+	// TODO: child might not have focusLast
+	// TODO: maybe adding toolbar/list should somewhat intercept those?
+	focusLast() {
+		if ( this.children.length ) {
+			this.children.last.focusLast();
+		}
+	}
 }

+ 6 - 26
packages/ckeditor5-ui/src/dropdown/list/createlistdropdown.js

@@ -7,12 +7,10 @@
  * @module ui/dropdown/list/createlistdropdown
  */
 
-import ListView from '../../list/listview';
-import ListItemView from '../../list/listitemview';
-
 import {
-	closeDropdownOnBlur, closeDropdownOnExecute, createButtonForDropdown, createDropdownView,
-	focusDropdownContentsOnArrows
+	addDefaultBehavior,
+	addListViewToDropdown,
+	createSingleButtonDropdown
 } from '../utils';
 
 /**
@@ -54,28 +52,10 @@ import {
  * @returns {module:ui/dropdown/list/listdropdownview~ListDropdownView} The list dropdown view instance.
  */
 export default function createListDropdown( model, locale ) {
-	const buttonView = createButtonForDropdown( model, locale );
-	const dropdownView = createDropdownView( model, buttonView, locale );
-
-	const listView = dropdownView.listView = new ListView( locale );
-
-	listView.items.bindTo( model.items ).using( itemModel => {
-		const item = new ListItemView( locale );
-
-		// Bind all attributes of the model to the item view.
-		item.bind( ...Object.keys( itemModel ) ).to( itemModel );
-
-		return item;
-	} );
-
-	dropdownView.panelView.children.add( listView );
-
-	// TODO: better:
-	dropdownView.buttonView.delegate( 'execute' ).to( dropdownView.buttonView, 'select' );
+	const dropdownView = createSingleButtonDropdown( model, locale );
 
-	closeDropdownOnBlur( dropdownView );
-	closeDropdownOnExecute( dropdownView, listView.items );
-	focusDropdownContentsOnArrows( dropdownView, listView );
+	addListViewToDropdown( dropdownView, model, locale );
+	addDefaultBehavior( dropdownView );
 
 	return dropdownView;
 }

+ 105 - 12
packages/ckeditor5-ui/src/dropdown/utils.js

@@ -12,19 +12,21 @@ import SplitButtonView from '../button/splitbuttonview';
 import ButtonView from '../button/buttonview';
 import DropdownPanelView from './dropdownpanelview';
 import DropdownView from './dropdownview';
+import ToolbarView from '../toolbar/toolbarview';
+import ListView from '../list/listview';
+import ListItemView from '../list/listitemview';
 
 /**
  * Adds a behavior to a dropdownView that focuses dropdown panel view contents on keystrokes.
  *
  * @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
- * @param {module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable} panelViewContents
  */
-export function focusDropdownContentsOnArrows( dropdownView, panelViewContents ) {
+export function focusDropdownContentsOnArrows( dropdownView ) {
 	// If the dropdown panel is already open, the arrow down key should
 	// focus the first element in list.
 	dropdownView.keystrokes.set( 'arrowdown', ( data, cancel ) => {
 		if ( dropdownView.isOpen ) {
-			panelViewContents.focus();
+			dropdownView.panelView.focus();
 			cancel();
 		}
 	} );
@@ -33,7 +35,7 @@ export function focusDropdownContentsOnArrows( dropdownView, panelViewContents )
 	// focus the last element in the list.
 	dropdownView.keystrokes.set( 'arrowup', ( data, cancel ) => {
 		if ( dropdownView.isOpen ) {
-			panelViewContents.focusLast();
+			dropdownView.panelView.focusLast();
 			cancel();
 		}
 	} );
@@ -43,12 +45,8 @@ export function focusDropdownContentsOnArrows( dropdownView, panelViewContents )
  * Adds a behavior to a dropdownView that closes dropdown view on any view collection item's "execute" event.
  *
  * @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
- * @param {module:ui/viewcollection~ViewCollection} viewCollection
  */
-export function closeDropdownOnExecute( dropdownView, viewCollection ) {
-	// TODO: Delegate all events instead of just execute.
-	viewCollection.delegate( 'execute' ).to( dropdownView );
-
+export function closeDropdownOnExecute( dropdownView ) {
 	// Close the dropdown when one of the list items has been executed.
 	dropdownView.on( 'execute', () => {
 		dropdownView.isOpen = false;
@@ -80,6 +78,9 @@ export function createButtonForDropdown( model, locale ) {
 
 	buttonView.bind( 'label', 'isEnabled', 'withText', 'keystroke', 'tooltip', 'icon' ).to( model );
 
+	// Dropdown expects "select" event to show contents.
+	buttonView.delegate( 'execute' ).to( buttonView, 'select' );
+
 	return buttonView;
 }
 
@@ -89,6 +90,10 @@ export function createSplitButtonForDropdown( model, locale ) {
 	// TODO: check 'isOn' binding.
 	buttonView.bind( 'label', 'isOn', 'isEnabled', 'withText', 'keystroke', 'tooltip', 'icon' ).to( model );
 
+	// TODO: something wierd with binding
+	buttonView.buttonView.bind( 'isOn' ).to( model );
+	buttonView.buttonView.bind( 'tooltip' ).to( model );
+
 	return buttonView;
 }
 
@@ -99,9 +104,97 @@ export function createDropdownView( model, buttonView, locale ) {
 	dropdownView.bind( 'isEnabled' ).to( model );
 
 	// TODO: check 'isOn' binding.
-	// buttonView.bind( 'isOn' ).to( model, 'isOn', dropdownView, 'isOpen', ( isOn, isOpen ) => {
-	// 	return isOn || isOpen;
-	// } );
+	buttonView.bind( 'isOn' ).to( model, 'isOn', dropdownView, 'isOpen', ( isOn, isOpen ) => {
+		return isOn || isOpen;
+	} );
 
 	return dropdownView;
 }
+
+export function createSplitButtonDropdown( model, locale ) {
+	const splitButtonView = createSplitButtonForDropdown( model, locale );
+	const dropdownView = createDropdownView( model, splitButtonView, locale );
+
+	// Extend template to hide arrow from dropdown.
+	// TODO: enable this on normal button instead of hiding it
+	dropdownView.extendTemplate( {
+		attributes: {
+			class: 'ck-splitbutton-dropdown'
+		}
+	} );
+
+	return dropdownView;
+}
+
+export function createSingleButtonDropdown( model, locale ) {
+	const buttonView = createButtonForDropdown( model, locale );
+
+	return createDropdownView( model, buttonView, locale );
+}
+
+export function enableModelIfOneIsEnabled( model, observables ) {
+	model.bind( 'isEnabled' ).to(
+		// Bind to #isEnabled of each observable...
+		...getBindingTargets( observables, 'isEnabled' ),
+		// ...and set it true if any observable #isEnabled is true.
+		( ...areEnabled ) => areEnabled.some( isEnabled => isEnabled )
+	);
+}
+
+export function addListViewToDropdown( dropdownView, model, locale ) {
+	const listView = dropdownView.listView = new ListView( locale );
+
+	listView.items.bindTo( model.items ).using( itemModel => {
+		const item = new ListItemView( locale );
+
+		// Bind all attributes of the model to the item view.
+		item.bind( ...Object.keys( itemModel ) ).to( itemModel );
+
+		return item;
+	} );
+
+	dropdownView.panelView.children.add( listView );
+
+	listView.items.delegate( 'execute' ).to( dropdownView );
+
+	return listView;
+}
+
+export function addToolbarToDropdown( dropdownView, model ) {
+	const toolbarView = dropdownView.toolbarView = new ToolbarView();
+
+	// TODO verify className binding
+	toolbarView.bind( 'isVertical', 'className' ).to( model, 'isVertical', 'toolbarClassName' );
+
+	// TODO: verify class names
+	dropdownView.extendTemplate( {
+		attributes: {
+			class: [ 'ck-buttondropdown' ]
+		}
+	} );
+
+	dropdownView.panelView.children.add( toolbarView );
+
+	// TODO: make it as 'items', 'views' ???
+	model.buttons.map( view => toolbarView.items.add( view ) );
+
+	return toolbarView;
+}
+
+export function addDefaultBehavior( dropdownView ) {
+	closeDropdownOnBlur( dropdownView );
+	closeDropdownOnExecute( dropdownView );
+	focusDropdownContentsOnArrows( dropdownView );
+}
+
+// Returns an array of binding components for
+// {@link module:utils/observablemixin~Observable#bind} from a set of iterable
+// buttons.
+//
+// @private
+// @param {Iterable.<module:ui/button/buttonview~ButtonView>} buttons
+// @param {String} attribute
+// @returns {Array.<String>}
+function getBindingTargets( buttons, attribute ) {
+	return Array.prototype.concat( ...buttons.map( button => [ button, attribute ] ) );
+}

+ 0 - 45
packages/ckeditor5-ui/tests/dropdown/button/createbuttondropdown.js

@@ -170,50 +170,5 @@ describe( 'createButtonDropdown', () => {
 				sinon.assert.calledOnce( spy );
 			} );
 		} );
-
-		describe( 'icon', () => {
-			it( 'should be set to first button\'s icon if no defaultIcon defined', () => {
-				expect( view.buttonView.icon ).to.equal( view.toolbarView.items.get( 0 ).icon );
-			} );
-
-			it( 'should be bound to first button that is on', () => {
-				view.toolbarView.items.get( 1 ).isOn = true;
-
-				expect( view.buttonView.icon ).to.equal( view.toolbarView.items.get( 1 ).icon );
-
-				view.toolbarView.items.get( 0 ).isOn = true;
-				view.toolbarView.items.get( 1 ).isOn = false;
-
-				expect( view.buttonView.icon ).to.equal( view.toolbarView.items.get( 0 ).icon );
-			} );
-
-			it( 'should be set to defaultIcon if defined and on button is on', () => {
-				const model = new Model( {
-					defaultIcon: '<svg>baz</svg>',
-					buttons
-				} );
-
-				view = createButtonDropdown( model, locale );
-				view.render();
-
-				expect( view.buttonView.icon ).to.equal( '<svg>baz</svg>' );
-			} );
-
-			it( 'should not bind icons if staticIcon is set', () => {
-				const model = new Model( {
-					defaultIcon: '<svg>baz</svg>',
-					staticIcon: true,
-					buttons
-				} );
-
-				view = createButtonDropdown( model, locale );
-				view.render();
-
-				expect( view.buttonView.icon ).to.equal( '<svg>baz</svg>' );
-				view.toolbarView.items.get( 1 ).isOn = true;
-
-				expect( view.buttonView.icon ).to.equal( '<svg>baz</svg>' );
-			} );
-		} );
 	} );
 } );