_baseEditor.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. import { CellCoords } from './../3rdparty/walkontable/src';
  2. import { stringify } from './../helpers/mixed';
  3. export const EditorState = {
  4. VIRGIN: 'STATE_VIRGIN', // before editing
  5. EDITING: 'STATE_EDITING',
  6. WAITING: 'STATE_WAITING', // waiting for async validation
  7. FINISHED: 'STATE_FINISHED'
  8. };
  9. /**
  10. * @util
  11. * @class BaseEditor
  12. */
  13. function BaseEditor(instance) {
  14. this.instance = instance;
  15. this.state = EditorState.VIRGIN;
  16. this._opened = false;
  17. this._fullEditMode = false;
  18. this._closeCallback = null;
  19. this.init();
  20. }
  21. BaseEditor.prototype._fireCallbacks = function(result) {
  22. if (this._closeCallback) {
  23. this._closeCallback(result);
  24. this._closeCallback = null;
  25. }
  26. };
  27. BaseEditor.prototype.init = function() {};
  28. BaseEditor.prototype.getValue = function() {
  29. throw Error('Editor getValue() method unimplemented');
  30. };
  31. BaseEditor.prototype.setValue = function() {
  32. throw Error('Editor setValue() method unimplemented');
  33. };
  34. BaseEditor.prototype.open = function() {
  35. throw Error('Editor open() method unimplemented');
  36. };
  37. BaseEditor.prototype.close = function() {
  38. throw Error('Editor close() method unimplemented');
  39. };
  40. BaseEditor.prototype.prepare = function(row, col, prop, td, originalValue, cellProperties) {
  41. this.TD = td;
  42. this.row = row;
  43. this.col = col;
  44. this.prop = prop;
  45. this.originalValue = originalValue;
  46. this.cellProperties = cellProperties;
  47. this.state = EditorState.VIRGIN;
  48. };
  49. BaseEditor.prototype.extend = function() {
  50. const baseClass = this.constructor;
  51. function Editor(...args) {
  52. baseClass.apply(this, args);
  53. }
  54. function inherit(Child, Parent) {
  55. function Bridge() {}
  56. Bridge.prototype = Parent.prototype;
  57. Child.prototype = new Bridge();
  58. Child.prototype.constructor = Child;
  59. return Child;
  60. }
  61. return inherit(Editor, baseClass);
  62. };
  63. BaseEditor.prototype.saveValue = function(value, ctrlDown) {
  64. let selection;
  65. let tmp;
  66. // if ctrl+enter and multiple cells selected, behave like Excel (finish editing and apply to all cells)
  67. if (ctrlDown) {
  68. selection = this.instance.getSelectedLast();
  69. if (selection[0] > selection[2]) {
  70. tmp = selection[0];
  71. selection[0] = selection[2];
  72. selection[2] = tmp;
  73. }
  74. if (selection[1] > selection[3]) {
  75. tmp = selection[1];
  76. selection[1] = selection[3];
  77. selection[3] = tmp;
  78. }
  79. } else {
  80. selection = [this.row, this.col, null, null];
  81. }
  82. this.instance.populateFromArray(selection[0], selection[1], value, selection[2], selection[3], 'edit');
  83. };
  84. BaseEditor.prototype.beginEditing = function(newInitialValue, event) {
  85. if (this.state !== EditorState.VIRGIN) {
  86. return;
  87. }
  88. this.instance.view.scrollViewport(new CellCoords(this.row, this.col));
  89. this.state = EditorState.EDITING;
  90. // Set the editor value only in the full edit mode. In other mode the focusable element has to be empty,
  91. // otherwise IME (editor for Asia users) doesn't work.
  92. if (this.isInFullEditMode()) {
  93. const stringifiedInitialValue = typeof newInitialValue === 'string' ? newInitialValue : stringify(this.originalValue);
  94. this.setValue(stringifiedInitialValue);
  95. }
  96. this.open(event);
  97. this._opened = true;
  98. this.focus();
  99. // only rerender the selections (FillHandle should disappear when beginediting is triggered)
  100. this.instance.view.render();
  101. this.instance.runHooks('afterBeginEditing', this.row, this.col);
  102. };
  103. BaseEditor.prototype.finishEditing = function(restoreOriginalValue, ctrlDown, callback) {
  104. const _this = this;
  105. let val;
  106. if (callback) {
  107. const previousCloseCallback = this._closeCallback;
  108. this._closeCallback = function(result) {
  109. if (previousCloseCallback) {
  110. previousCloseCallback(result);
  111. }
  112. callback(result);
  113. _this.instance.view.render();
  114. };
  115. }
  116. if (this.isWaiting()) {
  117. return;
  118. }
  119. if (this.state === EditorState.VIRGIN) {
  120. this.instance._registerTimeout(() => {
  121. _this._fireCallbacks(true);
  122. });
  123. return;
  124. }
  125. if (this.state === EditorState.EDITING) {
  126. if (restoreOriginalValue) {
  127. this.cancelChanges();
  128. this.instance.view.render();
  129. return;
  130. }
  131. const value = this.getValue();
  132. if (this.instance.getSettings().trimWhitespace) {
  133. // We trim only string values
  134. val = [
  135. [typeof value === 'string' ? String.prototype.trim.call(value || '') : value]
  136. ];
  137. } else {
  138. val = [
  139. [value]
  140. ];
  141. }
  142. this.state = EditorState.WAITING;
  143. this.saveValue(val, ctrlDown);
  144. if (this.instance.getCellValidator(this.cellProperties)) {
  145. this.instance.addHookOnce('postAfterValidate', (result) => {
  146. _this.state = EditorState.FINISHED;
  147. _this.discardEditor(result);
  148. });
  149. } else {
  150. this.state = EditorState.FINISHED;
  151. this.discardEditor(true);
  152. }
  153. }
  154. };
  155. BaseEditor.prototype.cancelChanges = function() {
  156. this.state = EditorState.FINISHED;
  157. this.discardEditor();
  158. };
  159. BaseEditor.prototype.discardEditor = function(result) {
  160. if (this.state !== EditorState.FINISHED) {
  161. return;
  162. }
  163. // validator was defined and failed
  164. if (result === false && this.cellProperties.allowInvalid !== true) {
  165. this.instance.selectCell(this.row, this.col);
  166. this.focus();
  167. this.state = EditorState.EDITING;
  168. this._fireCallbacks(false);
  169. } else {
  170. this.close();
  171. this._opened = false;
  172. this._fullEditMode = false;
  173. this.state = EditorState.VIRGIN;
  174. this._fireCallbacks(true);
  175. }
  176. };
  177. /**
  178. * Switch editor into full edit mode. In this state navigation keys don't close editor. This mode is activated
  179. * automatically after hit ENTER or F2 key on the cell or while editing cell press F2 key.
  180. */
  181. BaseEditor.prototype.enableFullEditMode = function() {
  182. this._fullEditMode = true;
  183. };
  184. /**
  185. * Checks if editor is in full edit mode.
  186. *
  187. * @returns {Boolean}
  188. */
  189. BaseEditor.prototype.isInFullEditMode = function() {
  190. return this._fullEditMode;
  191. };
  192. BaseEditor.prototype.isOpened = function() {
  193. return this._opened;
  194. };
  195. BaseEditor.prototype.isWaiting = function() {
  196. return this.state === EditorState.WAITING;
  197. };
  198. BaseEditor.prototype.checkEditorSection = function() {
  199. const totalRows = this.instance.countRows();
  200. let section = '';
  201. if (this.row < this.instance.getSettings().fixedRowsTop) {
  202. if (this.col < this.instance.getSettings().fixedColumnsLeft) {
  203. section = 'top-left-corner';
  204. } else {
  205. section = 'top';
  206. }
  207. } else if (this.instance.getSettings().fixedRowsBottom && this.row >= totalRows - this.instance.getSettings().fixedRowsBottom) {
  208. if (this.col < this.instance.getSettings().fixedColumnsLeft) {
  209. section = 'bottom-left-corner';
  210. } else {
  211. section = 'bottom';
  212. }
  213. } else if (this.col < this.instance.getSettings().fixedColumnsLeft) {
  214. section = 'left';
  215. }
  216. return section;
  217. };
  218. export default BaseEditor;