mobileTextEditor.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. import { KEY_CODES } from './../helpers/unicode';
  2. import { stopImmediatePropagation, isImmediatePropagationStopped } from './../helpers/dom/event';
  3. import { addClass, getScrollLeft, getScrollTop, hasClass, isChildOf, offset, outerHeight, outerWidth, removeClass, setCaretPosition } from './../helpers/dom/element';
  4. import BaseEditor from './_baseEditor';
  5. import EventManager from './../eventManager';
  6. var MobileTextEditor = BaseEditor.prototype.extend();
  7. var domDimensionsCache = {};
  8. /**
  9. * @private
  10. * @editor MobileTextEditor
  11. * @class MobileTextEditor
  12. */
  13. var createControls = function createControls() {
  14. this.controls = {};
  15. this.controls.leftButton = document.createElement('DIV');
  16. this.controls.leftButton.className = 'leftButton';
  17. this.controls.rightButton = document.createElement('DIV');
  18. this.controls.rightButton.className = 'rightButton';
  19. this.controls.upButton = document.createElement('DIV');
  20. this.controls.upButton.className = 'upButton';
  21. this.controls.downButton = document.createElement('DIV');
  22. this.controls.downButton.className = 'downButton';
  23. for (var button in this.controls) {
  24. if (Object.prototype.hasOwnProperty.call(this.controls, button)) {
  25. this.positionControls.appendChild(this.controls[button]);
  26. }
  27. }
  28. };
  29. MobileTextEditor.prototype.valueChanged = function () {
  30. return this.initValue != this.getValue();
  31. };
  32. MobileTextEditor.prototype.init = function () {
  33. var that = this;
  34. this.eventManager = new EventManager(this.instance);
  35. this.createElements();
  36. this.bindEvents();
  37. this.instance.addHook('afterDestroy', function () {
  38. that.destroy();
  39. });
  40. };
  41. MobileTextEditor.prototype.getValue = function () {
  42. return this.TEXTAREA.value;
  43. };
  44. MobileTextEditor.prototype.setValue = function (newValue) {
  45. this.initValue = newValue;
  46. this.TEXTAREA.value = newValue;
  47. };
  48. MobileTextEditor.prototype.createElements = function () {
  49. this.editorContainer = document.createElement('DIV');
  50. this.editorContainer.className = 'htMobileEditorContainer';
  51. this.cellPointer = document.createElement('DIV');
  52. this.cellPointer.className = 'cellPointer';
  53. this.moveHandle = document.createElement('DIV');
  54. this.moveHandle.className = 'moveHandle';
  55. this.inputPane = document.createElement('DIV');
  56. this.inputPane.className = 'inputs';
  57. this.positionControls = document.createElement('DIV');
  58. this.positionControls.className = 'positionControls';
  59. this.TEXTAREA = document.createElement('TEXTAREA');
  60. addClass(this.TEXTAREA, 'handsontableInput');
  61. this.inputPane.appendChild(this.TEXTAREA);
  62. this.editorContainer.appendChild(this.cellPointer);
  63. this.editorContainer.appendChild(this.moveHandle);
  64. this.editorContainer.appendChild(this.inputPane);
  65. this.editorContainer.appendChild(this.positionControls);
  66. createControls.call(this);
  67. document.body.appendChild(this.editorContainer);
  68. };
  69. MobileTextEditor.prototype.onBeforeKeyDown = function (event) {
  70. var instance = this;
  71. var that = instance.getActiveEditor();
  72. if (event.target !== that.TEXTAREA || isImmediatePropagationStopped(event)) {
  73. return;
  74. }
  75. switch (event.keyCode) {
  76. case KEY_CODES.ENTER:
  77. that.close();
  78. event.preventDefault(); // don't add newline to field
  79. break;
  80. case KEY_CODES.BACKSPACE:
  81. stopImmediatePropagation(event); // backspace, delete, home, end should only work locally when cell is edited (not in table context)
  82. break;
  83. default:
  84. break;
  85. }
  86. };
  87. MobileTextEditor.prototype.open = function () {
  88. this.instance.addHook('beforeKeyDown', this.onBeforeKeyDown);
  89. addClass(this.editorContainer, 'active');
  90. removeClass(this.cellPointer, 'hidden');
  91. this.updateEditorPosition();
  92. };
  93. MobileTextEditor.prototype.focus = function () {
  94. this.TEXTAREA.focus();
  95. setCaretPosition(this.TEXTAREA, this.TEXTAREA.value.length);
  96. };
  97. MobileTextEditor.prototype.close = function () {
  98. this.TEXTAREA.blur();
  99. this.instance.removeHook('beforeKeyDown', this.onBeforeKeyDown);
  100. removeClass(this.editorContainer, 'active');
  101. };
  102. MobileTextEditor.prototype.scrollToView = function () {
  103. var coords = this.instance.getSelectedRange().highlight;
  104. this.instance.view.scrollViewport(coords);
  105. };
  106. MobileTextEditor.prototype.hideCellPointer = function () {
  107. if (!hasClass(this.cellPointer, 'hidden')) {
  108. addClass(this.cellPointer, 'hidden');
  109. }
  110. };
  111. MobileTextEditor.prototype.updateEditorPosition = function (x, y) {
  112. if (x && y) {
  113. x = parseInt(x, 10);
  114. y = parseInt(y, 10);
  115. this.editorContainer.style.top = y + 'px';
  116. this.editorContainer.style.left = x + 'px';
  117. } else {
  118. var selection = this.instance.getSelected(),
  119. selectedCell = this.instance.getCell(selection[0], selection[1]);
  120. // cache sizes
  121. if (!domDimensionsCache.cellPointer) {
  122. domDimensionsCache.cellPointer = {
  123. height: outerHeight(this.cellPointer),
  124. width: outerWidth(this.cellPointer)
  125. };
  126. }
  127. if (!domDimensionsCache.editorContainer) {
  128. domDimensionsCache.editorContainer = {
  129. width: outerWidth(this.editorContainer)
  130. };
  131. }
  132. if (selectedCell !== undefined) {
  133. var scrollLeft = this.instance.view.wt.wtOverlays.leftOverlay.trimmingContainer == window ? 0 : getScrollLeft(this.instance.view.wt.wtOverlays.leftOverlay.holder);
  134. var scrollTop = this.instance.view.wt.wtOverlays.topOverlay.trimmingContainer == window ? 0 : getScrollTop(this.instance.view.wt.wtOverlays.topOverlay.holder);
  135. var selectedCellOffset = offset(selectedCell),
  136. selectedCellWidth = outerWidth(selectedCell),
  137. currentScrollPosition = {
  138. x: scrollLeft,
  139. y: scrollTop
  140. };
  141. this.editorContainer.style.top = parseInt(selectedCellOffset.top + outerHeight(selectedCell) - currentScrollPosition.y + domDimensionsCache.cellPointer.height, 10) + 'px';
  142. this.editorContainer.style.left = parseInt(window.innerWidth / 2 - domDimensionsCache.editorContainer.width / 2, 10) + 'px';
  143. if (selectedCellOffset.left + selectedCellWidth / 2 > parseInt(this.editorContainer.style.left, 10) + domDimensionsCache.editorContainer.width) {
  144. this.editorContainer.style.left = window.innerWidth - domDimensionsCache.editorContainer.width + 'px';
  145. } else if (selectedCellOffset.left + selectedCellWidth / 2 < parseInt(this.editorContainer.style.left, 10) + 20) {
  146. this.editorContainer.style.left = 0 + 'px';
  147. }
  148. this.cellPointer.style.left = parseInt(selectedCellOffset.left - domDimensionsCache.cellPointer.width / 2 - offset(this.editorContainer).left + selectedCellWidth / 2 - currentScrollPosition.x, 10) + 'px';
  149. }
  150. }
  151. };
  152. MobileTextEditor.prototype.updateEditorData = function () {
  153. var selected = this.instance.getSelected(),
  154. selectedValue = this.instance.getDataAtCell(selected[0], selected[1]);
  155. this.row = selected[0];
  156. this.col = selected[1];
  157. this.setValue(selectedValue);
  158. this.updateEditorPosition();
  159. };
  160. MobileTextEditor.prototype.prepareAndSave = function () {
  161. var val;
  162. if (!this.valueChanged()) {
  163. return;
  164. }
  165. if (this.instance.getSettings().trimWhitespace) {
  166. val = [[String.prototype.trim.call(this.getValue())]];
  167. } else {
  168. val = [[this.getValue()]];
  169. }
  170. this.saveValue(val);
  171. };
  172. MobileTextEditor.prototype.bindEvents = function () {
  173. var that = this;
  174. this.eventManager.addEventListener(this.controls.leftButton, 'touchend', function (event) {
  175. that.prepareAndSave();
  176. that.instance.selection.transformStart(0, -1, null, true);
  177. that.updateEditorData();
  178. event.preventDefault();
  179. });
  180. this.eventManager.addEventListener(this.controls.rightButton, 'touchend', function (event) {
  181. that.prepareAndSave();
  182. that.instance.selection.transformStart(0, 1, null, true);
  183. that.updateEditorData();
  184. event.preventDefault();
  185. });
  186. this.eventManager.addEventListener(this.controls.upButton, 'touchend', function (event) {
  187. that.prepareAndSave();
  188. that.instance.selection.transformStart(-1, 0, null, true);
  189. that.updateEditorData();
  190. event.preventDefault();
  191. });
  192. this.eventManager.addEventListener(this.controls.downButton, 'touchend', function (event) {
  193. that.prepareAndSave();
  194. that.instance.selection.transformStart(1, 0, null, true);
  195. that.updateEditorData();
  196. event.preventDefault();
  197. });
  198. this.eventManager.addEventListener(this.moveHandle, 'touchstart', function (event) {
  199. if (event.touches.length == 1) {
  200. var touch = event.touches[0];
  201. var onTouchPosition = {
  202. x: that.editorContainer.offsetLeft,
  203. y: that.editorContainer.offsetTop
  204. };
  205. var onTouchOffset = {
  206. x: touch.pageX - onTouchPosition.x,
  207. y: touch.pageY - onTouchPosition.y
  208. };
  209. that.eventManager.addEventListener(this, 'touchmove', function (event) {
  210. var touch = event.touches[0];
  211. that.updateEditorPosition(touch.pageX - onTouchOffset.x, touch.pageY - onTouchOffset.y);
  212. that.hideCellPointer();
  213. event.preventDefault();
  214. });
  215. }
  216. });
  217. this.eventManager.addEventListener(document.body, 'touchend', function (event) {
  218. if (!isChildOf(event.target, that.editorContainer) && !isChildOf(event.target, that.instance.rootElement)) {
  219. that.close();
  220. }
  221. });
  222. this.eventManager.addEventListener(this.instance.view.wt.wtOverlays.leftOverlay.holder, 'scroll', function (event) {
  223. if (that.instance.view.wt.wtOverlays.leftOverlay.trimmingContainer != window) {
  224. that.hideCellPointer();
  225. }
  226. });
  227. this.eventManager.addEventListener(this.instance.view.wt.wtOverlays.topOverlay.holder, 'scroll', function (event) {
  228. if (that.instance.view.wt.wtOverlays.topOverlay.trimmingContainer != window) {
  229. that.hideCellPointer();
  230. }
  231. });
  232. };
  233. MobileTextEditor.prototype.destroy = function () {
  234. this.eventManager.clear();
  235. this.editorContainer.parentNode.removeChild(this.editorContainer);
  236. };
  237. export default MobileTextEditor;