buttonview.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module ui/button/buttonview
  7. */
  8. import View from '../view';
  9. import IconView from '../icon/iconview';
  10. import TooltipView from '../tooltip/tooltipview';
  11. import { getEnvKeystrokeText } from '@ckeditor/ckeditor5-utils/src/keyboard';
  12. import '../../theme/components/button/button.css';
  13. /**
  14. * The button view class.
  15. *
  16. * const view = new ButtonView();
  17. *
  18. * view.set( {
  19. * label: 'A button',
  20. * keystroke: 'Ctrl+B',
  21. * tooltip: true,
  22. * withText: true
  23. * } );
  24. *
  25. * view.render();
  26. *
  27. * document.body.append( view.element );
  28. *
  29. * @extends module:ui/view~View
  30. */
  31. export default class ButtonView extends View {
  32. /**
  33. * @inheritDoc
  34. */
  35. constructor( locale ) {
  36. super( locale );
  37. const bind = this.bindTemplate;
  38. /**
  39. * The label of the button view visible to the user when {@link #withText} is `true`.
  40. * It can also be used to create a {@link #tooltip}.
  41. *
  42. * @observable
  43. * @member {String} #label
  44. */
  45. this.set( 'label' );
  46. /**
  47. * (Optional) The keystroke associated with the button, i.e. <kbd>CTRL+B</kbd>,
  48. * in the string format compatible with {@link module:utils/keyboard}.
  49. *
  50. * @observable
  51. * @member {Boolean} #keystroke
  52. */
  53. this.set( 'keystroke' );
  54. /**
  55. * (Optional) Tooltip of the button, i.e. displayed when hovering the button with the mouse cursor.
  56. *
  57. * * If defined as a `Boolean` (e.g. `true`), then combination of `label` and `keystroke` will be set as a tooltip.
  58. * * If defined as a `String`, tooltip will equal the exact text of that `String`.
  59. * * If defined as a `Function`, `label` and `keystroke` will be passed to that function, which is to return
  60. * a string with the tooltip text.
  61. *
  62. * const view = new ButtonView( locale );
  63. * view.tooltip = ( label, keystroke ) => `A tooltip for ${ label } and ${ keystroke }.`
  64. *
  65. * @observable
  66. * @default false
  67. * @member {Boolean|String|Function} #tooltip
  68. */
  69. this.set( 'tooltip' );
  70. /**
  71. * (Optional) The position of the tooltip. See {@link module:ui/tooltip/tooltipview~TooltipView#position}
  72. * to learn more about the available position values.
  73. *
  74. * **Note:** It makes sense only when the {@link #tooltip `tooltip` attribute} is defined.
  75. *
  76. * @observable
  77. * @default 's'
  78. * @member {'s'|'n'} #position
  79. */
  80. this.set( 'tooltipPosition', 's' );
  81. /**
  82. * The HTML type of the button. Default `button`.
  83. *
  84. * @observable
  85. * @member {'button'|'submit'|'reset'|'menu'} #type
  86. */
  87. this.set( 'type', 'button' );
  88. /**
  89. * Controls whether the button view is "on". It makes sense when a feature it represents
  90. * is currently active, e.g. a bold button is "on" when the selection is in the bold text.
  91. *
  92. * To disable the button, use {@link #isEnabled} instead.
  93. *
  94. * @observable
  95. * @member {Boolean} #isOn
  96. */
  97. this.set( 'isOn', false );
  98. /**
  99. * Controls whether the button view is enabled, i.e. it can be clicked and execute an action.
  100. *
  101. * To change the "on" state of the button, use {@link #isOn} instead.
  102. *
  103. * @observable
  104. * @member {Boolean} #isEnabled
  105. */
  106. this.set( 'isEnabled', true );
  107. /**
  108. * Controls whether the button view is visible. Visible by default, buttons are hidden
  109. * using a CSS class.
  110. *
  111. * @observable
  112. * @member {Boolean} #isVisible
  113. */
  114. this.set( 'isVisible', true );
  115. /**
  116. * (Optional) Controls whether the label of the button is hidden (e.g. an icon–only button).
  117. *
  118. * @observable
  119. * @member {Boolean} #withText
  120. */
  121. this.set( 'withText', false );
  122. /**
  123. * (Optional) An XML {@link module:ui/icon/iconview~IconView#content content} of the icon.
  124. * When defined, an {@link #iconView} will be added to the button.
  125. *
  126. * @observable
  127. * @member {String} #icon
  128. */
  129. this.set( 'icon' );
  130. /**
  131. * (Optional) Controls the `tabindex` HTML attribute of the button. By default, the button is focusable
  132. * but does not included in the <kbd>Tab</kbd> order.
  133. *
  134. * @observable
  135. * @default -1
  136. * @member {String} #tabindex
  137. */
  138. this.set( 'tabindex', -1 );
  139. /**
  140. * Collection of the child views inside of the button {@link #element}.
  141. *
  142. * @readonly
  143. * @member {module:ui/viewcollection~ViewCollection}
  144. */
  145. this.children = this.createCollection();
  146. /**
  147. * Tooltip of the button view. It is configurable using the {@link #tooltip tooltip attribute}.
  148. *
  149. * @readonly
  150. * @member {module:ui/tooltip/tooltipview~TooltipView} #tooltipView
  151. */
  152. this.tooltipView = this._createTooltipView();
  153. /**
  154. * Label of the button view. It is configurable using the {@link #label label attribute}.
  155. *
  156. * @readonly
  157. * @member {module:ui/view~View} #labelView
  158. */
  159. this.labelView = this._createLabelView();
  160. /**
  161. * Tooltip of the button bound to the template.
  162. *
  163. * @see #tooltip
  164. * @see #_getTooltipString
  165. * @private
  166. * @observable
  167. * @member {Boolean} #_tooltipString
  168. */
  169. this.bind( '_tooltipString' ).to(
  170. this, 'tooltip',
  171. this, 'label',
  172. this, 'keystroke',
  173. this._getTooltipString.bind( this )
  174. );
  175. /**
  176. * (Optional) The icon view of the button. Only present when the {@link #icon icon attribute} is defined.
  177. *
  178. * @readonly
  179. * @member {module:ui/icon/iconview~IconView} #iconView
  180. */
  181. this.setTemplate( {
  182. tag: 'button',
  183. attributes: {
  184. class: [
  185. 'ck-button',
  186. bind.to( 'isEnabled', value => value ? 'ck-enabled' : 'ck-disabled' ),
  187. bind.if( 'isVisible', 'ck-hidden', value => !value ),
  188. bind.to( 'isOn', value => value ? 'ck-on' : 'ck-off' ),
  189. bind.if( 'withText', 'ck-button_with-text' )
  190. ],
  191. type: bind.to( 'type', value => value ? value : 'button' ),
  192. tabindex: bind.to( 'tabindex' )
  193. },
  194. children: this.children,
  195. on: {
  196. mousedown: bind.to( evt => {
  197. evt.preventDefault();
  198. } ),
  199. click: bind.to( evt => {
  200. // We can't make the button disabled using the disabled attribute, because it won't be focusable.
  201. // Though, shouldn't this condition be moved to the button controller?
  202. if ( this.isEnabled ) {
  203. this.fire( 'execute' );
  204. } else {
  205. // Prevent the default when button is disabled, to block e.g.
  206. // automatic form submitting. See ckeditor/ckeditor5-link#74.
  207. evt.preventDefault();
  208. }
  209. } )
  210. }
  211. } );
  212. /**
  213. * Fired when the button view is clicked. It won't be fired when the button {@link #isEnabled}
  214. * is `false`.
  215. *
  216. * @event execute
  217. */
  218. }
  219. /**
  220. * @inheritDoc
  221. */
  222. render() {
  223. super.render();
  224. if ( this.icon ) {
  225. const iconView = this.iconView = new IconView();
  226. iconView.bind( 'content' ).to( this, 'icon' );
  227. this.children.add( iconView );
  228. }
  229. this.children.add( this.tooltipView );
  230. this.children.add( this.labelView );
  231. }
  232. /**
  233. * Focuses the {@link #element} of the button.
  234. */
  235. focus() {
  236. this.element.focus();
  237. }
  238. /**
  239. * Creates a {@link module:ui/tooltip/tooltipview~TooltipView} instance and binds it with button
  240. * attributes.
  241. *
  242. * @private
  243. * @returns {module:ui/tooltip/tooltipview~TooltipView}
  244. */
  245. _createTooltipView() {
  246. const tooltipView = new TooltipView();
  247. tooltipView.bind( 'text' ).to( this, '_tooltipString' );
  248. tooltipView.bind( 'position' ).to( this, 'tooltipPosition' );
  249. return tooltipView;
  250. }
  251. /**
  252. * Creates a label view instance and binds it with button attributes.
  253. *
  254. * @private
  255. * @returns {module:ui/view~View}
  256. */
  257. _createLabelView() {
  258. const labelView = new View();
  259. labelView.setTemplate( {
  260. tag: 'span',
  261. attributes: {
  262. class: [ 'ck-button__label' ]
  263. },
  264. children: [
  265. {
  266. text: this.bindTemplate.to( 'label' )
  267. }
  268. ]
  269. } );
  270. return labelView;
  271. }
  272. /**
  273. * Gets the text for the {@link #tooltipView} from the combination of
  274. * {@link #tooltip}, {@link #label} and {@link #keystroke} attributes.
  275. *
  276. * @private
  277. * @see #tooltip
  278. * @see #_tooltipString
  279. * @param {Boolean|String|Function} tooltip Button tooltip.
  280. * @param {String} label Button label.
  281. * @param {String} keystroke Button keystroke.
  282. * @returns {String}
  283. */
  284. _getTooltipString( tooltip, label, keystroke ) {
  285. if ( tooltip ) {
  286. if ( typeof tooltip == 'string' ) {
  287. return tooltip;
  288. } else {
  289. if ( keystroke ) {
  290. keystroke = getEnvKeystrokeText( keystroke );
  291. }
  292. if ( tooltip instanceof Function ) {
  293. return tooltip( label, keystroke );
  294. } else {
  295. return `${ label }${ keystroke ? ` (${ keystroke })` : '' }`;
  296. }
  297. }
  298. }
  299. return '';
  300. }
  301. }