8
0

highlightui.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module highlight/highlightui
  7. */
  8. import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
  9. import HighlightEditing from './highlightediting';
  10. import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
  11. import markerIcon from './../theme/icons/marker.svg';
  12. import penIcon from './../theme/icons/pen.svg';
  13. import eraserIcon from './../theme/icons/eraser.svg';
  14. import Model from '@ckeditor/ckeditor5-ui/src/model';
  15. import addToolbarToDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/addtoolbartodropdown';
  16. import closeDropdownOnBlur from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonblur';
  17. import closeDropdownOnExecute from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/closedropdownonexecute';
  18. import createDropdownView from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/createdropdownview';
  19. import createSplitButtonForDropdown from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/createsplitbuttonfordropdown';
  20. import enableModelIfOneIsEnabled from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/enablemodelifoneisenabled';
  21. import focusDropdownContentsOnArrows from '@ckeditor/ckeditor5-ui/src/dropdown/helpers/focusdropdowncontentsonarrows';
  22. import ToolbarSeparatorView from '@ckeditor/ckeditor5-ui/src/toolbar/toolbarseparatorview';
  23. import './../theme/highlight.css';
  24. /**
  25. * The default Highlight UI plugin.
  26. *
  27. * @extends module:core/plugin~Plugin
  28. */
  29. export default class HighlightUI extends Plugin {
  30. /**
  31. * @inheritDoc
  32. */
  33. static get requires() {
  34. return [ HighlightEditing ];
  35. }
  36. /**
  37. * @inheritDoc
  38. */
  39. static get pluginName() {
  40. return 'HighlightUI';
  41. }
  42. /**
  43. * @inheritDoc
  44. */
  45. init() {
  46. const options = this.editor.config.get( 'highlight.options' );
  47. for ( const option of options ) {
  48. this._addHighlighterButton( option );
  49. }
  50. this._addRemoveHighlightButton();
  51. this._addDropdown( options );
  52. }
  53. /**
  54. * Creates remove highlight button.
  55. *
  56. * @private
  57. */
  58. _addRemoveHighlightButton() {
  59. const t = this.editor.t;
  60. this._addButton( 'removeHighlight', t( 'Remove highlighting' ), eraserIcon );
  61. }
  62. /**
  63. * Creates toolbar button from provided highlight option.
  64. *
  65. * @param {module:highlight/highlightediting~HighlightOption} option
  66. * @private
  67. */
  68. _addHighlighterButton( option ) {
  69. const command = this.editor.commands.get( 'highlight' );
  70. this._addButton( 'highlight:' + option.model, option.title, getIconForType( option.type ), option.model, decorateHighlightButton );
  71. function decorateHighlightButton( button ) {
  72. button.bind( 'isEnabled' ).to( command, 'isEnabled' );
  73. button.bind( 'isOn' ).to( command, 'value', value => value === option.model );
  74. button.iconView.extendTemplate( {
  75. attributes: {
  76. style: `color: ${ option.color }`
  77. }
  78. } );
  79. }
  80. }
  81. /**
  82. * Internal method for creating highlight buttons.
  83. *
  84. * @param {String} name Name of a button.
  85. * @param {String} label Label for button.
  86. * @param {String} icon Button's icon.
  87. * @param {Function} [decorateButton=()=>{}] Additional method for extending button.
  88. * @private
  89. */
  90. _addButton( name, label, icon, value, decorateButton = () => {} ) {
  91. const editor = this.editor;
  92. editor.ui.componentFactory.add( name, locale => {
  93. const buttonView = new ButtonView( locale );
  94. buttonView.set( {
  95. label,
  96. icon,
  97. tooltip: true
  98. } );
  99. buttonView.on( 'execute', () => {
  100. editor.execute( 'highlight', { value } );
  101. editor.editing.view.focus();
  102. } );
  103. // Add additional behavior for buttonView.
  104. decorateButton( buttonView );
  105. return buttonView;
  106. } );
  107. }
  108. /**
  109. * Creates split button dropdown UI from provided highlight options.
  110. *
  111. * @param {Array.<module:highlight/highlightediting~HighlightOption>} options
  112. * @private
  113. */
  114. _addDropdown( options ) {
  115. const editor = this.editor;
  116. const t = editor.t;
  117. const componentFactory = editor.ui.componentFactory;
  118. const startingHighlighter = options[ 0 ];
  119. const optionsMap = options.reduce( ( retVal, option ) => {
  120. retVal[ option.model ] = option;
  121. return retVal;
  122. }, {} );
  123. componentFactory.add( 'highlightDropdown', locale => {
  124. const command = editor.commands.get( 'highlight' );
  125. const model = new Model( {
  126. tooltip: t( 'Highlight' ),
  127. withText: false,
  128. isVertical: false,
  129. // Holds last executed highlighter.
  130. lastExecuted: startingHighlighter.model,
  131. // Holds current highlighter to execute (might be different then last used).
  132. commandValue: startingHighlighter.model
  133. } );
  134. // Dropdown button changes to selection (command.value).
  135. // If selection is in highlight it get active highlight appearance (icon, color).
  136. // Otherwise it gets appearance (icon, color) of last executed highlight.
  137. model.bind( 'icon' ).to( command, 'value', value => getIconForType( getActiveOption( value, 'type' ) ) );
  138. model.bind( 'color' ).to( command, 'value', value => getActiveOption( value, 'color' ) );
  139. model.bind( 'commandValue' ).to( command, 'value', value => getActiveOption( value, 'model' ) );
  140. // Create buttons array.
  141. const buttons = options.map( option => {
  142. // Get existing highlighter button.
  143. const buttonView = componentFactory.create( 'highlight:' + option.model );
  144. // Update lastExecutedHighlight on execute.
  145. this.listenTo( buttonView, 'execute', () => model.set( { lastExecuted: option.model } ) );
  146. return buttonView;
  147. } );
  148. // Make toolbar button enabled when any button in dropdown is enabled before adding separator and eraser.
  149. enableModelIfOneIsEnabled( model, buttons );
  150. // Add separator and eraser buttons to dropdown.
  151. buttons.push( new ToolbarSeparatorView() );
  152. buttons.push( componentFactory.create( 'removeHighlight' ) );
  153. model.set( 'buttons', buttons );
  154. const splitButtonView = createSplitButtonForDropdown( model, locale );
  155. const dropdownView = createDropdownView( model, splitButtonView, locale );
  156. // TODO: Extend template to hide arrow from dropdown. Remove me after changes in theme-lark.
  157. dropdownView.extendTemplate( {
  158. attributes: {
  159. class: 'ck-splitbutton-dropdown'
  160. }
  161. } );
  162. addToolbarToDropdown( dropdownView, model );
  163. closeDropdownOnBlur( dropdownView );
  164. closeDropdownOnExecute( dropdownView );
  165. focusDropdownContentsOnArrows( dropdownView );
  166. bindIconStyleToColor( dropdownView, model );
  167. dropdownView.extendTemplate( {
  168. attributes: {
  169. class: [ 'ck-highlight-dropdown' ]
  170. }
  171. } );
  172. // Returns active highlighter option depending on current command value.
  173. // If current is not set or it is the same as last execute this method will return the option key (like icon or color)
  174. // of last executed highlighter. Otherwise it will return option key for current one.
  175. function getActiveOption( current, key ) {
  176. const whichHighlighter = !current || current === model.lastExecuted ? model.lastExecuted : current;
  177. return optionsMap[ whichHighlighter ][ key ];
  178. }
  179. return dropdownView;
  180. } );
  181. }
  182. }
  183. // Extends split button icon style to reflect last used button style.
  184. function bindIconStyleToColor( dropdownView, model ) {
  185. const iconView = dropdownView.buttonView.actionView.iconView;
  186. const bind = iconView.bindTemplate;
  187. iconView.extendTemplate( {
  188. attributes: {
  189. style: bind.to( 'color', color => `color:${ color }` )
  190. }
  191. } );
  192. iconView.bind( 'color' ).to( model, 'color' );
  193. }
  194. // Returns icon for given highlighter type.
  195. function getIconForType( type ) {
  196. return type === 'marker' ? markerIcon : penIcon;
  197. }