/** * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. * For licensing, see LICENSE.md. */ /** * @module ui/dropdown/button/splitbuttonview */ import View from '../../view'; import ButtonView from '../../button/buttonview'; import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler'; import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker'; import dropdownArrowIcon from '../../../theme/icons/dropdown-arrow.svg'; import '../../../theme/components/dropdown/splitbutton.css'; /** * The split button view class. * * const view = new SplitButtonView(); * * view.set( { * label: 'A button', * keystroke: 'Ctrl+B', * tooltip: true * } ); * * view.render(); * * document.body.append( view.element ); * * Also see {@link module:ui/dropdown/utils~createDropdown}. * * @extends module:ui/view~View * @implements module:ui/dropdown/button/dropdownbuttoninterface~DropdownButtonInterface */ export default class SplitButtonView extends View { /** * @inheritDoc */ constructor( locale ) { super( locale ); /** * Controls whether the button view is enabled, i.e. it can be clicked and execute an action. * * To change the "on" state of the button, use {@link #isOn} instead. * * @observable * @member {Boolean} #isEnabled */ this.set( 'isEnabled', true ); /** * Used to create a {@link #tooltip}. * * @observable * @member {String} #label */ this.set( 'label' ); /** * (Optional) The keystroke associated with the button, i.e. Ctrl+B, * in the string format compatible with {@link module:utils/keyboard}. * * @observable * @member {Boolean} #keystroke */ this.set( 'keystroke' ); /** * (Optional) An XML {@link module:ui/icon/iconview~IconView#content content} of the icon. * When defined, an {@link module:ui/button/buttonview~ButtonView#iconView} will be added to the {@link #actionView} button. * * @observable * @member {String} #icon */ this.set( 'icon' ); /** * (Optional) Tooltip of the button, i.e. displayed when hovering the button with the mouse cursor. * * * If defined as a `Boolean` (e.g. `true`), then combination of `label` and `keystroke` will be set as a tooltip. * * If defined as a `String`, tooltip will equal the exact text of that `String`. * * If defined as a `Function`, `label` and `keystroke` will be passed to that function, which is to return * a string with the tooltip text. * * const view = new ButtonView( locale ); * view.tooltip = ( label, keystroke ) => `A tooltip for ${ label } and ${ keystroke }.` * * @observable * @default false * @member {Boolean|String|Function} #tooltip */ this.set( 'tooltip' ); /** * Controls whether the button view is "on". It makes sense when a feature it represents * is currently active, e.g. a bold button is "on" when the selection is in the bold text. * * To disable the button, use {@link #isEnabled} instead. * * @observable * @member {Boolean} #isOn */ this.set( 'isOn', false ); /** * Controls whether the button view is enabled, i.e. it can be clicked and execute an action. * * To change the "on" state of the button, use {@link #isOn} instead. * * @observable * @member {Boolean} #isEnabled */ this.set( 'isEnabled', true ); /** * Collection of the child views inside of the split button {@link #element}. * * @readonly * @member {module:ui/viewcollection~ViewCollection} */ this.children = this.createCollection(); /** * A main button of split button. * * @readonly * @member {module:ui/button/buttonview~ButtonView} */ this.actionView = this._createActionView(); /** * A secondary button of split button that opens dropdown. * * @readonly * @member {module:ui/button/buttonview~ButtonView} */ this.arrowView = this._createArrowView(); /** * Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. It manages * keystrokes of the split button: * * * moves focus to arrow view when action view is focused, * * moves focus to action view when arrow view is focused. * * @readonly * @member {module:utils/keystrokehandler~KeystrokeHandler} */ this.keystrokes = new KeystrokeHandler(); /** * Tracks information about DOM focus in the dropdown. * * @readonly * @member {module:utils/focustracker~FocusTracker} */ this.focusTracker = new FocusTracker(); this.setTemplate( { tag: 'div', attributes: { class: 'ck-splitbutton' }, children: this.children } ); } /** * @inheritDoc */ render() { super.render(); this.children.add( this.actionView ); this.children.add( this.arrowView ); this.focusTracker.add( this.actionView.element ); this.focusTracker.add( this.arrowView.element ); this.keystrokes.listenTo( this.element ); // Overrides toolbar focus cycling behavior. this.keystrokes.set( 'arrowright', ( evt, cancel ) => { if ( this.focusTracker.focusedElement === this.actionView.element ) { this.arrowView.focus(); cancel(); } } ); // Overrides toolbar focus cycling behavior. this.keystrokes.set( 'arrowleft', ( evt, cancel ) => { if ( this.focusTracker.focusedElement === this.arrowView.element ) { this.actionView.focus(); cancel(); } } ); } /** * Focuses the {@link #actionView#element} of the action part of split button. */ focus() { this.actionView.focus(); } /** * Creates a {@link module:ui/button/buttonview~ButtonView} instance as {@link #actionView} and binds it with main split button * attributes. * * @private * @returns {module:ui/button/buttonview~ButtonView} */ _createActionView() { const buttonView = new ButtonView(); buttonView.bind( 'icon', 'isEnabled', 'label', 'isOn', 'tooltip', 'keystroke' ).to( this ); buttonView.delegate( 'execute' ).to( this ); return buttonView; } /** * Creates a {@link module:ui/button/buttonview~ButtonView} instance as {@link #arrowView} and binds it with main split button * attributes. * * @private * @returns {module:ui/button/buttonview~ButtonView} */ _createArrowView() { const arrowView = new ButtonView(); arrowView.icon = dropdownArrowIcon; arrowView.extendTemplate( { attributes: { class: 'ck-splitbutton-arrow' } } ); arrowView.bind( 'isEnabled' ).to( this ); arrowView.delegate( 'execute' ).to( this, 'open' ); return arrowView; } }