dropdownview.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /**
  6. * @module ui/dropdown/dropdownview
  7. */
  8. import View from '../view';
  9. import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
  10. import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
  11. /**
  12. * The dropdown view class.
  13. *
  14. * const button = new ButtonView( locale );
  15. * const panel = new DropdownPanelView( locale );
  16. * const dropdown = new DropdownView( locale, button, panel );
  17. *
  18. * panel.element.textContent = 'Content of the panel';
  19. * button.set( {
  20. * label: 'A dropdown',
  21. * withText: true
  22. * } );
  23. *
  24. * dropdown.render();
  25. *
  26. * // Will render a dropdown with a panel containing a "Content of the panel" text.
  27. * document.body.appendChild( dropdown.element );
  28. *
  29. * Also see {@link module:ui/dropdown/createdropdown~createDropdown} and
  30. * {@link module:ui/dropdown/list/createlistdropdown~createListDropdown} to learn about different
  31. * dropdown creation helpers.
  32. *
  33. * @extends module:ui/view~View
  34. */
  35. export default class DropdownView extends View {
  36. /**
  37. * @inheritDoc
  38. */
  39. constructor( locale, buttonView, panelView ) {
  40. super( locale );
  41. // Extend button's template before it's registered as a child of the dropdown because
  42. // by doing so, its #element is rendered and any post–render template extension will
  43. // not be reflected in DOM.
  44. buttonView.extendTemplate( {
  45. attributes: {
  46. class: [
  47. 'ck-dropdown__button'
  48. ]
  49. }
  50. } );
  51. /**
  52. * Button of the dropdown view. Clicking the button opens the {@link #panelView}.
  53. *
  54. * @readonly
  55. * @member {module:ui/button/buttonview~ButtonView} #buttonView
  56. */
  57. this.buttonView = buttonView;
  58. /**
  59. * Panel of the dropdown. It opens when the {@link #buttonView} is
  60. * {@link module:ui/button/buttonview~ButtonView#event:execute executed} (i.e. clicked).
  61. *
  62. * Child views can be added to the panel's `children` collection:
  63. *
  64. * dropdown.panelView.children.add( childView );
  65. *
  66. * See {@link module:ui/dropdown/dropdownpanelview~DropdownPanelView#children} and
  67. * {@link module:ui/viewcollection~ViewCollection#add}.
  68. *
  69. * @readonly
  70. * @member {module:ui/dropdown/dropdownpanelview~DropdownPanelView} #panelView
  71. */
  72. this.panelView = panelView;
  73. /**
  74. * Controls whether the dropdown view is open, i.e. shows or hides the {@link #panelView panel}.
  75. *
  76. * @observable
  77. * @member {Boolean} #isOpen
  78. */
  79. this.set( 'isOpen', false );
  80. /**
  81. * Tracks information about DOM focus in the dropdown.
  82. *
  83. * @readonly
  84. * @member {module:utils/focustracker~FocusTracker}
  85. */
  86. this.focusTracker = new FocusTracker();
  87. /**
  88. * Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. It manages
  89. * keystrokes of the dropdown:
  90. *
  91. * * <kbd>▼</kbd> opens the dropdown,
  92. * * <kbd>◀</kbd> and <kbd>Esc</kbd> closes the dropdown.
  93. *
  94. * @readonly
  95. * @member {module:utils/keystrokehandler~KeystrokeHandler}
  96. */
  97. this.keystrokes = new KeystrokeHandler();
  98. this.setTemplate( {
  99. tag: 'div',
  100. attributes: {
  101. class: [
  102. 'ck-dropdown'
  103. ]
  104. },
  105. children: [
  106. buttonView,
  107. panelView
  108. ]
  109. } );
  110. }
  111. /**
  112. * @inheritDoc
  113. */
  114. render() {
  115. super.render();
  116. // Toggle the the dropdown when it's button has been clicked.
  117. this.listenTo( this.buttonView, 'execute', () => {
  118. this.isOpen = !this.isOpen;
  119. } );
  120. // Toggle the visibility of the panel when the dropdown becomes open.
  121. this.panelView.bind( 'isVisible' ).to( this, 'isOpen' );
  122. // Listen for keystrokes coming from within #element.
  123. this.keystrokes.listenTo( this.element );
  124. // Register #element in the focus tracker.
  125. this.focusTracker.add( this.element );
  126. const closeDropdown = ( data, cancel ) => {
  127. if ( this.isOpen ) {
  128. this.buttonView.focus();
  129. this.isOpen = false;
  130. cancel();
  131. }
  132. };
  133. // Open the dropdown panel using the arrow down key, just like with return or space.
  134. this.keystrokes.set( 'arrowdown', ( data, cancel ) => {
  135. // Don't open if the dropdown is disabled or already open.
  136. if ( this.buttonView.isEnabled && !this.isOpen ) {
  137. this.isOpen = true;
  138. cancel();
  139. }
  140. } );
  141. // Block the right arrow key (until nested dropdowns are implemented).
  142. this.keystrokes.set( 'arrowright', ( data, cancel ) => {
  143. if ( this.isOpen ) {
  144. cancel();
  145. }
  146. } );
  147. // Close the dropdown using the arrow left/escape key.
  148. this.keystrokes.set( 'arrowleft', closeDropdown );
  149. this.keystrokes.set( 'esc', closeDropdown );
  150. }
  151. /**
  152. * Focuses the {@link #buttonView}.
  153. */
  154. focus() {
  155. this.buttonView.focus();
  156. }
  157. }