tableView.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. import {
  2. addClass,
  3. empty,
  4. fastInnerHTML,
  5. fastInnerText,
  6. getScrollbarWidth,
  7. hasClass,
  8. isChildOf,
  9. isInput,
  10. isOutsideInput
  11. } from './helpers/dom/element';
  12. import EventManager from './eventManager';
  13. import { stopPropagation, isImmediatePropagationStopped, isRightClick, isLeftClick } from './helpers/dom/event';
  14. import Walkontable from './3rdparty/walkontable/src';
  15. import { handleMouseEvent } from './selection/mouseEventHandler';
  16. /**
  17. * Cross-platform helper to clear text selection.
  18. */
  19. const clearTextSelection = function() {
  20. // http://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript
  21. if (window.getSelection) {
  22. if (window.getSelection().empty) { // Chrome
  23. window.getSelection().empty();
  24. } else if (window.getSelection().removeAllRanges) { // Firefox
  25. window.getSelection().removeAllRanges();
  26. }
  27. } else if (document.selection) { // IE?
  28. document.selection.empty();
  29. }
  30. };
  31. /**
  32. * Handsontable TableView constructor
  33. * @param {Object} instance
  34. */
  35. function TableView(instance) {
  36. const that = this;
  37. this.eventManager = new EventManager(instance);
  38. this.instance = instance;
  39. this.settings = instance.getSettings();
  40. this.selectionMouseDown = false;
  41. const originalStyle = instance.rootElement.getAttribute('style');
  42. if (originalStyle) {
  43. instance.rootElement.setAttribute('data-originalstyle', originalStyle); // needed to retrieve original style in jsFiddle link generator in HT examples. may be removed in future versions
  44. }
  45. addClass(instance.rootElement, 'handsontable');
  46. const table = document.createElement('TABLE');
  47. addClass(table, 'htCore');
  48. if (instance.getSettings().tableClassName) {
  49. addClass(table, instance.getSettings().tableClassName);
  50. }
  51. this.THEAD = document.createElement('THEAD');
  52. table.appendChild(this.THEAD);
  53. this.TBODY = document.createElement('TBODY');
  54. table.appendChild(this.TBODY);
  55. instance.table = table;
  56. instance.container.insertBefore(table, instance.container.firstChild);
  57. this.eventManager.addEventListener(instance.rootElement, 'mousedown', (event) => {
  58. this.selectionMouseDown = true;
  59. if (!that.isTextSelectionAllowed(event.target)) {
  60. clearTextSelection();
  61. event.preventDefault();
  62. window.focus(); // make sure that window that contains HOT is active. Important when HOT is in iframe.
  63. }
  64. });
  65. this.eventManager.addEventListener(instance.rootElement, 'mouseup', () => {
  66. this.selectionMouseDown = false;
  67. });
  68. this.eventManager.addEventListener(instance.rootElement, 'mousemove', (event) => {
  69. if (this.selectionMouseDown && !that.isTextSelectionAllowed(event.target)) {
  70. // Clear selection only when fragmentSelection is enabled, otherwise clearing selection breakes the IME editor.
  71. if (this.settings.fragmentSelection) {
  72. clearTextSelection();
  73. }
  74. event.preventDefault();
  75. }
  76. });
  77. this.eventManager.addEventListener(document.documentElement, 'keyup', (event) => {
  78. if (instance.selection.isInProgress() && !event.shiftKey) {
  79. instance.selection.finish();
  80. }
  81. });
  82. let isMouseDown;
  83. this.isMouseDown = function() {
  84. return isMouseDown;
  85. };
  86. this.eventManager.addEventListener(document.documentElement, 'mouseup', (event) => {
  87. if (instance.selection.isInProgress() && isLeftClick(event)) { // is left mouse button
  88. instance.selection.finish();
  89. }
  90. isMouseDown = false;
  91. if (isOutsideInput(document.activeElement) || (!instance.selection.isSelected() && !isRightClick(event))) {
  92. instance.unlisten();
  93. }
  94. });
  95. this.eventManager.addEventListener(document.documentElement, 'contextmenu', (event) => {
  96. if (instance.selection.isInProgress() && isRightClick(event)) {
  97. instance.selection.finish();
  98. isMouseDown = false;
  99. }
  100. });
  101. this.eventManager.addEventListener(document.documentElement, 'touchend', () => {
  102. if (instance.selection.isInProgress()) {
  103. instance.selection.finish();
  104. }
  105. isMouseDown = false;
  106. });
  107. this.eventManager.addEventListener(document.documentElement, 'mousedown', (event) => {
  108. const originalTarget = event.target;
  109. const eventX = event.x || event.clientX;
  110. const eventY = event.y || event.clientY;
  111. let next = event.target;
  112. if (isMouseDown || !instance.rootElement) {
  113. return; // it must have been started in a cell
  114. }
  115. // immediate click on "holder" means click on the right side of vertical scrollbar
  116. if (next === instance.view.wt.wtTable.holder) {
  117. const scrollbarWidth = getScrollbarWidth();
  118. if (document.elementFromPoint(eventX + scrollbarWidth, eventY) !== instance.view.wt.wtTable.holder ||
  119. document.elementFromPoint(eventX, eventY + scrollbarWidth) !== instance.view.wt.wtTable.holder) {
  120. return;
  121. }
  122. } else {
  123. while (next !== document.documentElement) {
  124. if (next === null) {
  125. if (event.isTargetWebComponent) {
  126. break;
  127. }
  128. // click on something that was a row but now is detached (possibly because your click triggered a rerender)
  129. return;
  130. }
  131. if (next === instance.rootElement) {
  132. // click inside container
  133. return;
  134. }
  135. next = next.parentNode;
  136. }
  137. }
  138. // function did not return until here, we have an outside click!
  139. const outsideClickDeselects = typeof that.settings.outsideClickDeselects === 'function' ?
  140. that.settings.outsideClickDeselects(originalTarget) :
  141. that.settings.outsideClickDeselects;
  142. if (outsideClickDeselects) {
  143. instance.deselectCell();
  144. } else {
  145. instance.destroyEditor(false, false, true);
  146. instance.unlisten();// 点击tab等其它handsontable外的元素时,handsontable要解除监听键盘等事件. handsontable只在mouseup事件通过isOutsideInput只针对input 等几个输入元素
  147. }
  148. });
  149. this.eventManager.addEventListener(table, 'selectstart', (event) => {
  150. if (that.settings.fragmentSelection || isInput(event.target)) {
  151. return;
  152. }
  153. // https://github.com/handsontable/handsontable/issues/160
  154. // Prevent text from being selected when performing drag down.
  155. event.preventDefault();
  156. });
  157. const walkontableConfig = {
  158. debug: () => that.settings.debug,
  159. externalRowCalculator: this.instance.getPlugin('autoRowSize') && this.instance.getPlugin('autoRowSize').isEnabled(),
  160. table,
  161. preventOverflow: () => this.settings.preventOverflow,
  162. stretchH: () => that.settings.stretchH,
  163. data: instance.getDataAtCell,
  164. totalRows: () => instance.countRows(),
  165. totalColumns: () => instance.countCols(),
  166. fixedColumnsLeft: () => that.settings.fixedColumnsLeft,
  167. fixedRowsTop: () => that.settings.fixedRowsTop,
  168. fixedRowsBottom: () => that.settings.fixedRowsBottom,
  169. minSpareRows: () => that.settings.minSpareRows,
  170. renderAllRows: that.settings.renderAllRows,
  171. rowHeaders: () => {
  172. const headerRenderers = [];
  173. if (instance.hasRowHeaders()) {
  174. headerRenderers.push((row, TH) => that.appendRowHeader(row, TH));
  175. }
  176. instance.runHooks('afterGetRowHeaderRenderers', headerRenderers);
  177. return headerRenderers;
  178. },
  179. columnHeaders: () => {
  180. const headerRenderers = [];
  181. if (instance.hasColHeaders()) {
  182. headerRenderers.push((column, TH) => {
  183. that.appendColHeader(column, TH);
  184. });
  185. }
  186. instance.runHooks('afterGetColumnHeaderRenderers', headerRenderers);
  187. return headerRenderers;
  188. },
  189. columnWidth: instance.getColWidth,
  190. rowHeight: instance.getRowHeight,
  191. cellRenderer(row, col, TD) {
  192. const cellProperties = that.instance.getCellMeta(row, col);
  193. const prop = that.instance.colToProp(col);
  194. let value = that.instance.getDataAtRowProp(row, prop);
  195. if (that.instance.hasHook('beforeValueRender')) {
  196. value = that.instance.runHooks('beforeValueRender', value, cellProperties);
  197. }
  198. that.instance.runHooks('beforeRenderer', TD, row, col, prop, value, cellProperties);
  199. that.instance.getCellRenderer(cellProperties)(that.instance, TD, row, col, prop, value, cellProperties);
  200. that.instance.runHooks('afterRenderer', TD, row, col, prop, value, cellProperties);
  201. },
  202. selections: that.instance.selection.highlight,
  203. hideBorderOnMouseDownOver: () => that.settings.fragmentSelection,
  204. onCellMouseDown: (event, coords, TD, wt) => {
  205. const blockCalculations = {
  206. row: false,
  207. column: false,
  208. cell: false
  209. };
  210. instance.listen();
  211. that.activeWt = wt;
  212. isMouseDown = true;
  213. instance.runHooks('beforeOnCellMouseDown', event, coords, TD, blockCalculations);
  214. if (isImmediatePropagationStopped(event)) {
  215. return;
  216. }
  217. handleMouseEvent(event, {
  218. coords,
  219. selection: instance.selection,
  220. controller: blockCalculations,
  221. });
  222. instance.runHooks('afterOnCellMouseDown', event, coords, TD);
  223. that.activeWt = that.wt;
  224. },
  225. onCellContextMenu: (event, coords, TD, wt) => {
  226. that.activeWt = wt;
  227. isMouseDown = false;
  228. if (instance.selection.isInProgress()) {
  229. instance.selection.finish();
  230. }
  231. instance.runHooks('beforeOnCellContextMenu', event, coords, TD);
  232. if (isImmediatePropagationStopped(event)) {
  233. return;
  234. }
  235. instance.runHooks('afterOnCellContextMenu', event, coords, TD);
  236. that.activeWt = that.wt;
  237. },
  238. onCellMouseOut: (event, coords, TD, wt) => {
  239. that.activeWt = wt;
  240. instance.runHooks('beforeOnCellMouseOut', event, coords, TD);
  241. if (isImmediatePropagationStopped(event)) {
  242. return;
  243. }
  244. instance.runHooks('afterOnCellMouseOut', event, coords, TD);
  245. that.activeWt = that.wt;
  246. },
  247. onCellMouseOver: (event, coords, TD, wt) => {
  248. const blockCalculations = {
  249. row: false,
  250. column: false,
  251. cell: false
  252. };
  253. that.activeWt = wt;
  254. instance.runHooks('beforeOnCellMouseOver', event, coords, TD, blockCalculations);
  255. if (isImmediatePropagationStopped(event)) {
  256. return;
  257. }
  258. if (isMouseDown) {
  259. handleMouseEvent(event, {
  260. coords,
  261. selection: instance.selection,
  262. controller: blockCalculations,
  263. });
  264. }
  265. instance.runHooks('afterOnCellMouseOver', event, coords, TD);
  266. that.activeWt = that.wt;
  267. },
  268. onCellMouseUp: (event, coords, TD, wt) => {
  269. that.activeWt = wt;
  270. instance.runHooks('beforeOnCellMouseUp', event, coords, TD);
  271. instance.runHooks('afterOnCellMouseUp', event, coords, TD);
  272. that.activeWt = that.wt;
  273. },
  274. onCellCornerMouseDown(event) {
  275. event.preventDefault();
  276. instance.runHooks('afterOnCellCornerMouseDown', event);
  277. },
  278. onCellCornerDblClick(event) {
  279. event.preventDefault();
  280. instance.runHooks('afterOnCellCornerDblClick', event);
  281. },
  282. beforeDraw(force, skipRender) {
  283. that.beforeRender(force, skipRender);
  284. },
  285. onDraw(force) {
  286. that.onDraw(force);
  287. },
  288. onScrollVertically() {
  289. instance.runHooks('afterScrollVertically');
  290. },
  291. onScrollHorizontally() {
  292. instance.runHooks('afterScrollHorizontally');
  293. },
  294. onBeforeRemoveCellClassNames: () => instance.runHooks('beforeRemoveCellClassNames'),
  295. onAfterDrawSelection: (currentRow, currentColumn, cornersOfSelection, layerLevel) => instance.runHooks('afterDrawSelection',
  296. currentRow, currentColumn, cornersOfSelection, layerLevel),
  297. onBeforeDrawBorders(corners, borderClassName) {
  298. instance.runHooks('beforeDrawBorders', corners, borderClassName);
  299. },
  300. onBeforeTouchScroll() {
  301. instance.runHooks('beforeTouchScroll');
  302. },
  303. onAfterMomentumScroll() {
  304. instance.runHooks('afterMomentumScroll');
  305. },
  306. onBeforeStretchingColumnWidth: (stretchedWidth, column) => instance.runHooks('beforeStretchingColumnWidth', stretchedWidth, column),
  307. onModifyRowHeaderWidth: rowHeaderWidth => instance.runHooks('modifyRowHeaderWidth', rowHeaderWidth),
  308. onModifyGetCellCoords: (row, column, topmost) => instance.runHooks('modifyGetCellCoords', row, column, topmost),
  309. viewportRowCalculatorOverride(calc) {
  310. const rows = instance.countRows();
  311. let viewportOffset = that.settings.viewportRowRenderingOffset;
  312. if (viewportOffset === 'auto' && that.settings.fixedRowsTop) {
  313. viewportOffset = 10;
  314. }
  315. if (typeof viewportOffset === 'number') {
  316. calc.startRow = Math.max(calc.startRow - viewportOffset, 0);
  317. calc.endRow = Math.min(calc.endRow + viewportOffset, rows - 1);
  318. }
  319. if (viewportOffset === 'auto') {
  320. const center = calc.startRow + calc.endRow - calc.startRow;
  321. const offset = Math.ceil(center / rows * 12);
  322. calc.startRow = Math.max(calc.startRow - offset, 0);
  323. calc.endRow = Math.min(calc.endRow + offset, rows - 1);
  324. }
  325. instance.runHooks('afterViewportRowCalculatorOverride', calc);
  326. },
  327. viewportColumnCalculatorOverride(calc) {
  328. const cols = instance.countCols();
  329. let viewportOffset = that.settings.viewportColumnRenderingOffset;
  330. if (viewportOffset === 'auto' && that.settings.fixedColumnsLeft) {
  331. viewportOffset = 10;
  332. }
  333. if (typeof viewportOffset === 'number') {
  334. calc.startColumn = Math.max(calc.startColumn - viewportOffset, 0);
  335. calc.endColumn = Math.min(calc.endColumn + viewportOffset, cols - 1);
  336. }
  337. if (viewportOffset === 'auto') {
  338. const center = calc.startColumn + calc.endColumn - calc.startColumn;
  339. const offset = Math.ceil(center / cols * 12);
  340. calc.startRow = Math.max(calc.startColumn - offset, 0);
  341. calc.endColumn = Math.min(calc.endColumn + offset, cols - 1);
  342. }
  343. instance.runHooks('afterViewportColumnCalculatorOverride', calc);
  344. },
  345. rowHeaderWidth: () => that.settings.rowHeaderWidth,
  346. columnHeaderHeight() {
  347. const columnHeaderHeight = instance.runHooks('modifyColumnHeaderHeight');
  348. return that.settings.columnHeaderHeight || columnHeaderHeight;
  349. }
  350. };
  351. instance.runHooks('beforeInitWalkontable', walkontableConfig);
  352. this.wt = new Walkontable(walkontableConfig);
  353. this.activeWt = this.wt;
  354. this.eventManager.addEventListener(that.wt.wtTable.spreader, 'mousedown', (event) => {
  355. // right mouse button exactly on spreader means right click on the right hand side of vertical scrollbar
  356. if (event.target === that.wt.wtTable.spreader && event.which === 3) {
  357. stopPropagation(event);
  358. }
  359. });
  360. this.eventManager.addEventListener(that.wt.wtTable.spreader, 'contextmenu', (event) => {
  361. // right mouse button exactly on spreader means right click on the right hand side of vertical scrollbar
  362. if (event.target === that.wt.wtTable.spreader && event.which === 3) {
  363. stopPropagation(event);
  364. }
  365. });
  366. this.eventManager.addEventListener(document.documentElement, 'click', () => {
  367. if (that.settings.observeDOMVisibility) {
  368. if (that.wt.drawInterrupted) {
  369. that.instance.forceFullRender = true;
  370. that.render();
  371. }
  372. }
  373. });
  374. }
  375. TableView.prototype.isTextSelectionAllowed = function(el) {
  376. if (isInput(el)) {
  377. return true;
  378. }
  379. const isChildOfTableBody = isChildOf(el, this.instance.view.wt.wtTable.spreader);
  380. if (this.settings.fragmentSelection === true && isChildOfTableBody) {
  381. return true;
  382. }
  383. if (this.settings.fragmentSelection === 'cell' && this.isSelectedOnlyCell() && isChildOfTableBody) {
  384. return true;
  385. }
  386. if (!this.settings.fragmentSelection && this.isCellEdited() && this.isSelectedOnlyCell()) {
  387. return true;
  388. }
  389. return false;
  390. };
  391. /**
  392. * Check if selected only one cell.
  393. *
  394. * @returns {Boolean}
  395. */
  396. TableView.prototype.isSelectedOnlyCell = function() {
  397. const [row, col, rowEnd, colEnd] = this.instance.getSelectedLast() || [];
  398. return row !== void 0 && row === rowEnd && col === colEnd;
  399. };
  400. TableView.prototype.isCellEdited = function() {
  401. const activeEditor = this.instance.getActiveEditor();
  402. return activeEditor && activeEditor.isOpened();
  403. };
  404. TableView.prototype.beforeRender = function(force, skipRender) {
  405. if (force) {
  406. // this.instance.forceFullRender = did Handsontable request full render?
  407. this.instance.runHooks('beforeRender', this.instance.forceFullRender, skipRender);
  408. }
  409. };
  410. TableView.prototype.onDraw = function(force) {
  411. if (force) {
  412. // this.instance.forceFullRender = did Handsontable request full render?
  413. this.instance.runHooks('afterRender', this.instance.forceFullRender);
  414. }
  415. };
  416. TableView.prototype.render = function() {
  417. this.wt.draw(!this.instance.forceFullRender);
  418. this.instance.forceFullRender = false;
  419. this.instance.renderCall = false;
  420. };
  421. /**
  422. * Returns td object given coordinates
  423. *
  424. * @param {CellCoords} coords
  425. * @param {Boolean} topmost
  426. */
  427. TableView.prototype.getCellAtCoords = function(coords, topmost) {
  428. const td = this.wt.getCell(coords, topmost);
  429. if (td < 0) { // there was an exit code (cell is out of bounds)
  430. return null;
  431. }
  432. return td;
  433. };
  434. /**
  435. * Scroll viewport to a cell.
  436. *
  437. * @param {CellCoords} coords
  438. * @param {Boolean} [snapToTop]
  439. * @param {Boolean} [snapToRight]
  440. * @param {Boolean} [snapToBottom]
  441. * @param {Boolean} [snapToLeft]
  442. * @returns {Boolean}
  443. */
  444. TableView.prototype.scrollViewport = function(coords, snapToTop, snapToRight, snapToBottom, snapToLeft) {
  445. return this.wt.scrollViewport(coords, snapToTop, snapToRight, snapToBottom, snapToLeft);
  446. };
  447. /**
  448. * Scroll viewport to a column.
  449. *
  450. * @param {Number} column Visual column index.
  451. * @param {Boolean} [snapToLeft]
  452. * @param {Boolean} [snapToRight]
  453. * @returns {Boolean}
  454. */
  455. TableView.prototype.scrollViewportHorizontally = function(column, snapToRight, snapToLeft) {
  456. return this.wt.scrollViewportHorizontally(column, snapToRight, snapToLeft);
  457. };
  458. /**
  459. * Scroll viewport to a row.
  460. *
  461. * @param {Number} row Visual row index.
  462. * @param {Boolean} [snapToTop]
  463. * @param {Boolean} [snapToBottom]
  464. * @returns {Boolean}
  465. */
  466. TableView.prototype.scrollViewportVertically = function(row, snapToTop, snapToBottom) {
  467. return this.wt.scrollViewportVertically(row, snapToTop, snapToBottom);
  468. };
  469. /**
  470. * Append row header to a TH element
  471. * @param row
  472. * @param TH
  473. */
  474. TableView.prototype.appendRowHeader = function(row, TH) {
  475. if (TH.firstChild) {
  476. const container = TH.firstChild;
  477. if (!hasClass(container, 'relative')) {
  478. empty(TH);
  479. this.appendRowHeader(row, TH);
  480. return;
  481. }
  482. this.updateCellHeader(container.querySelector('.rowHeader'), row, this.instance.getRowHeader);
  483. } else {
  484. const div = document.createElement('div');
  485. const span = document.createElement('span');
  486. div.className = 'relative';
  487. span.className = 'rowHeader';
  488. this.updateCellHeader(span, row, this.instance.getRowHeader);
  489. div.appendChild(span);
  490. TH.appendChild(div);
  491. }
  492. this.instance.runHooks('afterGetRowHeader', row, TH);
  493. };
  494. /**
  495. * Append column header to a TH element
  496. * @param col
  497. * @param TH
  498. */
  499. TableView.prototype.appendColHeader = function(col, TH) {
  500. if (TH.firstChild) {
  501. const container = TH.firstChild;
  502. if (hasClass(container, 'relative')) {
  503. this.updateCellHeader(container.querySelector('.colHeader'), col, this.instance.getColHeader);
  504. } else {
  505. empty(TH);
  506. this.appendColHeader(col, TH);
  507. }
  508. } else {
  509. const div = document.createElement('div');
  510. const span = document.createElement('span');
  511. div.className = 'relative';
  512. span.className = 'colHeader';
  513. this.updateCellHeader(span, col, this.instance.getColHeader);
  514. div.appendChild(span);
  515. TH.appendChild(div);
  516. }
  517. this.instance.runHooks('afterGetColHeader', col, TH);
  518. };
  519. /**
  520. * Update header cell content
  521. *
  522. * @since 0.15.0-beta4
  523. * @param {HTMLElement} element Element to update
  524. * @param {Number} index Row index or column index
  525. * @param {Function} content Function which should be returns content for this cell
  526. */
  527. TableView.prototype.updateCellHeader = function(element, index, content) {
  528. let renderedIndex = index;
  529. const parentOverlay = this.wt.wtOverlays.getParentOverlay(element) || this.wt;
  530. // prevent wrong calculations from SampleGenerator
  531. if (element.parentNode) {
  532. if (hasClass(element, 'colHeader')) {
  533. renderedIndex = parentOverlay.wtTable.columnFilter.sourceToRendered(index);
  534. } else if (hasClass(element, 'rowHeader')) {
  535. renderedIndex = parentOverlay.wtTable.rowFilter.sourceToRendered(index);
  536. }
  537. }
  538. if (renderedIndex > -1) {
  539. fastInnerHTML(element, content(index));
  540. } else {
  541. // workaround for https://github.com/handsontable/handsontable/issues/1946
  542. fastInnerText(element, String.fromCharCode(160));
  543. addClass(element, 'cornerHeader');
  544. }
  545. };
  546. /**
  547. * Given a element's left position relative to the viewport, returns maximum element width until the right
  548. * edge of the viewport (before scrollbar)
  549. *
  550. * @param {Number} leftOffset
  551. * @return {Number}
  552. */
  553. TableView.prototype.maximumVisibleElementWidth = function(leftOffset) {
  554. const workspaceWidth = this.wt.wtViewport.getWorkspaceWidth();
  555. const maxWidth = workspaceWidth - leftOffset;
  556. return maxWidth > 0 ? maxWidth : 0;
  557. };
  558. /**
  559. * Given a element's top position relative to the viewport, returns maximum element height until the bottom
  560. * edge of the viewport (before scrollbar)
  561. *
  562. * @param {Number} topOffset
  563. * @return {Number}
  564. */
  565. TableView.prototype.maximumVisibleElementHeight = function(topOffset) {
  566. const workspaceHeight = this.wt.wtViewport.getWorkspaceHeight();
  567. const maxHeight = workspaceHeight - topOffset;
  568. return maxHeight > 0 ? maxHeight : 0;
  569. };
  570. TableView.prototype.mainViewIsActive = function() {
  571. return this.wt === this.activeWt;
  572. };
  573. TableView.prototype.destroy = function() {
  574. this.wt.destroy();
  575. this.eventManager.destroy();
  576. };
  577. export default TableView;