00bf3d6535184c8d1776e1829ab656a024240ea54dfbdcee101bdf38107eeeca723bce1966d2c351da5bfe8fb705bf758036940e0089d7d0b8df4be3c09795 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. import {CellCoords} from './3rdparty/walkontable/src';
  2. import {KEY_CODES, isMetaKey, isCtrlKey} from './helpers/unicode';
  3. import {stopPropagation, stopImmediatePropagation, isImmediatePropagationStopped} from './helpers/dom/event';
  4. import {getEditorInstance} from './editors';
  5. import EventManager from './eventManager';
  6. import {EditorState} from './editors/_baseEditor';
  7. function EditorManager(instance, priv, selection) {
  8. var _this = this,
  9. destroyed = false,
  10. eventManager,
  11. activeEditor;
  12. eventManager = new EventManager(instance);
  13. function moveSelectionAfterEnter(shiftKey) {
  14. selection.setSelectedHeaders(false, false, false);
  15. var enterMoves = typeof priv.settings.enterMoves === 'function' ? priv.settings.enterMoves(event) : priv.settings.enterMoves;
  16. if (shiftKey) {
  17. // move selection up
  18. selection.transformStart(-enterMoves.row, -enterMoves.col);
  19. } else {
  20. // move selection down (add a new row if needed)
  21. selection.transformStart(enterMoves.row, enterMoves.col, true);
  22. }
  23. }
  24. function moveSelectionUp(shiftKey) {
  25. if (shiftKey) {
  26. if (selection.selectedHeader.cols) {
  27. selection.setSelectedHeaders(selection.selectedHeader.rows, false, false);
  28. }
  29. selection.transformEnd(-1, 0);
  30. } else {
  31. selection.setSelectedHeaders(false, false, false);
  32. selection.transformStart(-1, 0);
  33. }
  34. }
  35. function moveSelectionDown(shiftKey) {
  36. if (shiftKey) {
  37. // expanding selection down with shift
  38. selection.transformEnd(1, 0);
  39. } else {
  40. selection.setSelectedHeaders(false, false, false);
  41. selection.transformStart(1, 0);
  42. }
  43. }
  44. function moveSelectionRight(shiftKey) {
  45. if (shiftKey) {
  46. selection.transformEnd(0, 1);
  47. } else {
  48. selection.setSelectedHeaders(false, false, false);
  49. selection.transformStart(0, 1);
  50. }
  51. }
  52. function moveSelectionLeft(shiftKey) {
  53. if (shiftKey) {
  54. if (selection.selectedHeader.rows) {
  55. selection.setSelectedHeaders(false, selection.selectedHeader.cols, false);
  56. }
  57. selection.transformEnd(0, -1);
  58. } else {
  59. selection.setSelectedHeaders(false, false, false);
  60. selection.transformStart(0, -1);
  61. }
  62. }
  63. function onKeyDown(event) {
  64. var ctrlDown,
  65. rangeModifier;
  66. if (!instance.isListening()) {
  67. return;
  68. }
  69. instance.runHooks('beforeKeyDown', event);
  70. if (destroyed) {
  71. return;
  72. }
  73. if (isImmediatePropagationStopped(event)) {
  74. return;
  75. }
  76. priv.lastKeyCode = event.keyCode;
  77. if (!selection.isSelected()) {
  78. return;
  79. }
  80. // catch CTRL but not right ALT (which in some systems triggers ALT+CTRL)
  81. ctrlDown = (event.ctrlKey || event.metaKey) && !event.altKey;
  82. if (activeEditor && !activeEditor.isWaiting()) {
  83. if (!isMetaKey(event.keyCode) && !isCtrlKey(event.keyCode) && !ctrlDown && !_this.isEditorOpened()) {
  84. _this.openEditor('', event);
  85. return;
  86. }
  87. }
  88. rangeModifier = event.shiftKey ? selection.setRangeEnd : selection.setRangeStart;
  89. switch (event.keyCode) {
  90. case KEY_CODES.A:
  91. if (!_this.isEditorOpened() && ctrlDown) {
  92. selection.selectAll();
  93. event.preventDefault();
  94. stopPropagation(event);
  95. }
  96. break;
  97. case KEY_CODES.ARROW_UP:
  98. if (_this.isEditorOpened() && !activeEditor.isWaiting()) {
  99. _this.closeEditorAndSaveChanges(ctrlDown);
  100. }
  101. moveSelectionUp(event.shiftKey);
  102. event.preventDefault();
  103. stopPropagation(event);
  104. break;
  105. case KEY_CODES.ARROW_DOWN:
  106. if (_this.isEditorOpened() && !activeEditor.isWaiting()) {
  107. _this.closeEditorAndSaveChanges(ctrlDown);
  108. }
  109. moveSelectionDown(event.shiftKey);
  110. event.preventDefault();
  111. stopPropagation(event);
  112. break;
  113. case KEY_CODES.ARROW_RIGHT:
  114. if (_this.isEditorOpened() && !activeEditor.isWaiting()) {
  115. _this.closeEditorAndSaveChanges(ctrlDown);
  116. }
  117. moveSelectionRight(event.shiftKey);
  118. event.preventDefault();
  119. stopPropagation(event);
  120. break;
  121. case KEY_CODES.ARROW_LEFT:
  122. if (_this.isEditorOpened() && !activeEditor.isWaiting()) {
  123. _this.closeEditorAndSaveChanges(ctrlDown);
  124. }
  125. moveSelectionLeft(event.shiftKey);
  126. event.preventDefault();
  127. stopPropagation(event);
  128. break;
  129. case KEY_CODES.TAB:
  130. selection.setSelectedHeaders(false, false, false);
  131. var tabMoves = typeof priv.settings.tabMoves === 'function' ? priv.settings.tabMoves(event) : priv.settings.tabMoves;
  132. if (event.shiftKey) {
  133. // move selection left
  134. selection.transformStart(-tabMoves.row, -tabMoves.col);
  135. } else {
  136. // move selection right (add a new column if needed)
  137. selection.transformStart(tabMoves.row, tabMoves.col, true);
  138. }
  139. event.preventDefault();
  140. stopPropagation(event);
  141. break;
  142. case KEY_CODES.BACKSPACE:
  143. case KEY_CODES.DELETE:
  144. selection.empty(event);
  145. _this.prepareEditor();
  146. event.preventDefault();
  147. break;
  148. case KEY_CODES.F2:
  149. /* F2 */
  150. _this.openEditor(null, event);
  151. if (activeEditor) {
  152. activeEditor.enableFullEditMode();
  153. }
  154. event.preventDefault(); // prevent Opera from opening 'Go to Page dialog'
  155. break;
  156. case KEY_CODES.ENTER:
  157. /* return/enter */
  158. if (_this.isEditorOpened()) {
  159. if (activeEditor && activeEditor.state !== EditorState.WAITING) {
  160. _this.closeEditorAndSaveChanges(ctrlDown);
  161. }
  162. moveSelectionAfterEnter(event.shiftKey);
  163. } else if (instance.getSettings().enterBeginsEditing) {
  164. _this.openEditor(null, event);
  165. if (activeEditor) {
  166. activeEditor.enableFullEditMode();
  167. }
  168. } else {
  169. moveSelectionAfterEnter(event.shiftKey);
  170. }
  171. event.preventDefault(); // don't add newline to field
  172. stopImmediatePropagation(event); // required by HandsontableEditor
  173. break;
  174. case KEY_CODES.ESCAPE:
  175. if (_this.isEditorOpened()) {
  176. _this.closeEditorAndRestoreOriginalValue(ctrlDown);
  177. }
  178. event.preventDefault();
  179. break;
  180. case KEY_CODES.HOME:
  181. selection.setSelectedHeaders(false, false, false);
  182. if (event.ctrlKey || event.metaKey) {
  183. rangeModifier(new CellCoords(0, priv.selRange.from.col));
  184. } else {
  185. rangeModifier(new CellCoords(priv.selRange.from.row, 0));
  186. }
  187. event.preventDefault(); // don't scroll the window
  188. stopPropagation(event);
  189. break;
  190. case KEY_CODES.END:
  191. selection.setSelectedHeaders(false, false, false);
  192. if (event.ctrlKey || event.metaKey) {
  193. rangeModifier(new CellCoords(instance.countRows() - 1, priv.selRange.from.col));
  194. } else {
  195. rangeModifier(new CellCoords(priv.selRange.from.row, instance.countCols() - 1));
  196. }
  197. event.preventDefault(); // don't scroll the window
  198. stopPropagation(event);
  199. break;
  200. case KEY_CODES.PAGE_UP:
  201. selection.setSelectedHeaders(false, false, false);
  202. selection.transformStart(-instance.countVisibleRows(), 0);
  203. event.preventDefault(); // don't page up the window
  204. stopPropagation(event);
  205. break;
  206. case KEY_CODES.PAGE_DOWN:
  207. selection.setSelectedHeaders(false, false, false);
  208. selection.transformStart(instance.countVisibleRows(), 0);
  209. event.preventDefault(); // don't page down the window
  210. stopPropagation(event);
  211. break;
  212. default:
  213. break;
  214. }
  215. }
  216. function init() {
  217. instance.addHook('afterDocumentKeyDown', onKeyDown);
  218. eventManager.addEventListener(document.documentElement, 'keydown', (event) => {
  219. if (!destroyed) {
  220. instance.runHooks('afterDocumentKeyDown', event);
  221. }
  222. });
  223. function onDblClick(event, coords, elem) {
  224. // may be TD or TH
  225. if (elem.nodeName == 'TD') {
  226. _this.openEditor();
  227. if (activeEditor) {
  228. activeEditor.enableFullEditMode();
  229. }
  230. }
  231. }
  232. instance.view.wt.update('onCellDblClick', onDblClick);
  233. instance.addHook('afterDestroy', () => {
  234. destroyed = true;
  235. });
  236. }
  237. /**
  238. * Destroy current editor, if exists.
  239. *
  240. * @function destroyEditor
  241. * @memberof! Handsontable.EditorManager#
  242. * @param {Boolean} revertOriginal
  243. */
  244. this.destroyEditor = function(revertOriginal) {
  245. this.closeEditor(revertOriginal);
  246. };
  247. /**
  248. * Get active editor.
  249. *
  250. * @function getActiveEditor
  251. * @memberof! Handsontable.EditorManager#
  252. * @returns {*}
  253. */
  254. this.getActiveEditor = function() {
  255. return activeEditor;
  256. };
  257. /**
  258. * Prepare text input to be displayed at given grid cell.
  259. *
  260. * @function prepareEditor
  261. * @memberof! Handsontable.EditorManager#
  262. */
  263. this.prepareEditor = function() {
  264. var row,
  265. col,
  266. prop,
  267. td,
  268. originalValue,
  269. cellProperties,
  270. editorClass;
  271. if (activeEditor && activeEditor.isWaiting()) {
  272. this.closeEditor(false, false, (dataSaved) => {
  273. if (dataSaved) {
  274. _this.prepareEditor();
  275. }
  276. });
  277. return;
  278. }
  279. row = priv.selRange.highlight.row;
  280. col = priv.selRange.highlight.col;
  281. prop = instance.colToProp(col);
  282. td = instance.getCell(row, col);
  283. originalValue = instance.getSourceDataAtCell(instance.runHooks('modifyRow', row), col);
  284. cellProperties = instance.getCellMeta(row, col);
  285. editorClass = instance.getCellEditor(cellProperties);
  286. if (editorClass) {
  287. activeEditor = getEditorInstance(editorClass, instance);
  288. activeEditor.prepare(row, col, prop, td, originalValue, cellProperties);
  289. } else {
  290. activeEditor = void 0;
  291. }
  292. };
  293. /**
  294. * Check is editor is opened/showed.
  295. *
  296. * @function isEditorOpened
  297. * @memberof! Handsontable.EditorManager#
  298. * @returns {Boolean}
  299. */
  300. this.isEditorOpened = function() {
  301. return activeEditor && activeEditor.isOpened();
  302. };
  303. /**
  304. * Open editor with initial value.
  305. *
  306. * @function openEditor
  307. * @memberof! Handsontable.EditorManager#
  308. * @param {String} initialValue
  309. * @param {DOMEvent} event
  310. */
  311. this.openEditor = function(initialValue, event) {
  312. if (activeEditor && !activeEditor.cellProperties.readOnly) {
  313. activeEditor.beginEditing(initialValue, event);
  314. } else if (activeEditor && activeEditor.cellProperties.readOnly) {
  315. // move the selection after opening the editor with ENTER key
  316. if (event && event.keyCode === KEY_CODES.ENTER) {
  317. moveSelectionAfterEnter();
  318. }
  319. }
  320. };
  321. /**
  322. * Close editor, finish editing cell.
  323. *
  324. * @function closeEditor
  325. * @memberof! Handsontable.EditorManager#
  326. * @param {Boolean} restoreOriginalValue
  327. * @param {Boolean} [ctrlDown]
  328. * @param {Function} [callback]
  329. */
  330. this.closeEditor = function(restoreOriginalValue, ctrlDown, callback) {
  331. if (activeEditor) {
  332. activeEditor.finishEditing(restoreOriginalValue, ctrlDown, callback);
  333. } else if (callback) {
  334. callback(false);
  335. }
  336. };
  337. /**
  338. * Close editor and save changes.
  339. *
  340. * @function closeEditorAndSaveChanges
  341. * @memberof! Handsontable.EditorManager#
  342. * @param {Boolean} ctrlDown
  343. */
  344. this.closeEditorAndSaveChanges = function(ctrlDown) {
  345. return this.closeEditor(false, ctrlDown);
  346. };
  347. /**
  348. * Close editor and restore original value.
  349. *
  350. * @function closeEditorAndRestoreOriginalValue
  351. * @memberof! Handsontable.EditorManager#
  352. * @param {Boolean} ctrlDown
  353. */
  354. this.closeEditorAndRestoreOriginalValue = function(ctrlDown) {
  355. return this.closeEditor(true, ctrlDown);
  356. };
  357. init();
  358. }
  359. export default EditorManager;