mouseselectionhandler.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /**
  6. * @module table/tableselection/mouseselectionhandler
  7. */
  8. import mix from '@ckeditor/ckeditor5-utils/src/mix';
  9. import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
  10. import { findAncestor } from '../commands/utils';
  11. import MouseEventsObserver from './mouseeventsobserver';
  12. /**
  13. * A mouse selection handler class for the table selection.
  14. *
  15. * It registers the {@link module:table/tableselection/mouseeventsobserver~MouseEventsObserver} to observe view document mouse events
  16. * and invoke proper {@link module:table/tableselection~TableSelection} actions.
  17. */
  18. export default class MouseSelectionHandler {
  19. /**
  20. * Creates an instance of the `MouseSelectionHandler`.
  21. *
  22. * @param {module:table/tableselection~TableSelection} tableSelection
  23. * @param {module:engine/controller/editingcontroller~EditingController} editing
  24. */
  25. constructor( tableSelection, editing ) {
  26. /**
  27. * The table selection plugin instance.
  28. *
  29. * @private
  30. * @readonly
  31. * @member {module:table/tableselection~TableSelection}
  32. */
  33. this._tableSelection = tableSelection;
  34. /**
  35. * A flag indicating that the mouse selection is "active". A selection is "active" if it was started and not yet finished.
  36. * A selection can be "active", for instance, if a user moves a mouse over a table while holding a mouse button down.
  37. *
  38. * @private
  39. * @readonly
  40. * @member {Boolean}
  41. */
  42. this._isSelecting = false;
  43. /**
  44. * Editing mapper.
  45. *
  46. * @private
  47. * @readonly
  48. * @member {module:engine/conversion/mapper~Mapper}
  49. */
  50. this._mapper = editing.mapper;
  51. const view = editing.view;
  52. // Currently the MouseObserver only handles `mouseup` events.
  53. view.addObserver( MouseEventsObserver );
  54. this.listenTo( view.document, 'mousedown', ( event, domEventData ) => this._handleMouseDown( domEventData ) );
  55. this.listenTo( view.document, 'mousemove', ( event, domEventData ) => this._handleMouseMove( domEventData ) );
  56. this.listenTo( view.document, 'mouseup', ( event, domEventData ) => this._handleMouseUp( domEventData ) );
  57. }
  58. /**
  59. * Handles starting a selection when "mousedown" event has table cell as a DOM target.
  60. *
  61. * If there is no table cell in the event target, it will clear the previous selection.
  62. *
  63. * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
  64. * @private
  65. */
  66. _handleMouseDown( domEventData ) {
  67. const tableCell = this._getModelTableCellFromDomEvent( domEventData );
  68. if ( !tableCell ) {
  69. this._tableSelection.clearSelection();
  70. return;
  71. }
  72. this._isSelecting = true;
  73. this._tableSelection.startSelectingFrom( tableCell );
  74. }
  75. /**
  76. * Handles updating the table selection when the "mousemove" event has a table cell as a DOM target.
  77. *
  78. * Does nothing if there is no table cell in event target or the selection has not been started yet.
  79. *
  80. * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
  81. * @private
  82. */
  83. _handleMouseMove( domEventData ) {
  84. if ( !this._isSelecting || !isButtonPressed( domEventData ) ) {
  85. this._tableSelection.stopSelection();
  86. return;
  87. }
  88. // https://github.com/ckeditor/ckeditor5/issues/6114
  89. domEventData.preventDefault();
  90. const tableCell = this._getModelTableCellFromDomEvent( domEventData );
  91. if ( !tableCell ) {
  92. return;
  93. }
  94. this._tableSelection.setSelectingTo( tableCell );
  95. }
  96. /**
  97. * Handles ending (not clearing) the table selection on the "mouseup" event.
  98. *
  99. * Does nothing if the selection has not been started yet.
  100. *
  101. * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
  102. * @private
  103. */
  104. _handleMouseUp( domEventData ) {
  105. if ( !this._isSelecting ) {
  106. return;
  107. }
  108. this._isSelecting = false;
  109. const tableCell = this._getModelTableCellFromDomEvent( domEventData );
  110. // Selection can be stopped if table cell is undefined.
  111. this._tableSelection.stopSelection( tableCell );
  112. }
  113. /**
  114. * Finds a model table cell for a given DOM event.
  115. *
  116. * @private
  117. * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
  118. * @returns {module:engine/model/element~Element|undefined} Returns model table cell or undefined event target is not
  119. * a mapped table cell.
  120. */
  121. _getModelTableCellFromDomEvent( domEventData ) {
  122. const viewTargetElement = domEventData.target;
  123. const modelElement = this._mapper.toModelElement( viewTargetElement );
  124. if ( !modelElement ) {
  125. return;
  126. }
  127. if ( modelElement.is( 'tableCell' ) ) {
  128. return modelElement;
  129. }
  130. return findAncestor( 'tableCell', modelElement );
  131. }
  132. }
  133. mix( MouseSelectionHandler, ObservableMixin );
  134. function isButtonPressed( domEventData ) {
  135. return !!domEventData.domEvent.buttons;
  136. }