7d4af6f55ca36b58f2e7c24c94641095e9b0799c52b12edb7f15fa9b097a3e04f8f965bce4dd5e811cecabca413564d54740c98a0f48ee4875d541e2567855 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import {
  2. innerHeight,
  3. innerWidth,
  4. getScrollLeft,
  5. getScrollTop,
  6. offset,
  7. } from './../../../helpers/dom/element';
  8. import {rangeEach, rangeEachReverse} from './../../../helpers/number';
  9. /**
  10. * @class Scroll
  11. */
  12. class Scroll {
  13. /**
  14. * @param {Walkontable} wotInstance
  15. */
  16. constructor(wotInstance) {
  17. this.wot = wotInstance;
  18. // legacy support
  19. this.instance = wotInstance;
  20. }
  21. /**
  22. * Scrolls viewport to a cell by minimum number of cells
  23. *
  24. * @param {CellCoords} coords
  25. */
  26. scrollViewport(coords) {
  27. if (!this.wot.drawn) {
  28. return;
  29. }
  30. const {
  31. topOverlay,
  32. leftOverlay,
  33. totalRows,
  34. totalColumns,
  35. fixedRowsTop,
  36. fixedRowsBottom,
  37. fixedColumnsLeft,
  38. } = this._getVariables();
  39. if (coords.row < 0 || coords.row > Math.max(totalRows - 1, 0)) {
  40. throw new Error(`row ${coords.row} does not exist`);
  41. }
  42. if (coords.col < 0 || coords.col > Math.max(totalColumns - 1, 0)) {
  43. throw new Error(`column ${coords.col} does not exist`);
  44. }
  45. if (coords.row >= fixedRowsTop && coords.row < this.getFirstVisibleRow()) {
  46. topOverlay.scrollTo(coords.row);
  47. } else if (coords.row > this.getLastVisibleRow() && coords.row < totalRows - fixedRowsBottom) {
  48. topOverlay.scrollTo(coords.row, true);
  49. }
  50. if (coords.col >= fixedColumnsLeft && coords.col < this.getFirstVisibleColumn()) {
  51. leftOverlay.scrollTo(coords.col);
  52. } else if (coords.col > this.getLastVisibleColumn()) {
  53. leftOverlay.scrollTo(coords.col, true);
  54. }
  55. }
  56. /**
  57. * Get first visible row based on virtual dom and how table is visible in browser window viewport.
  58. *
  59. * @returns {Number}
  60. */
  61. getFirstVisibleRow() {
  62. const {
  63. topOverlay,
  64. wtTable,
  65. wtViewport,
  66. totalRows,
  67. fixedRowsTop,
  68. } = this._getVariables();
  69. let firstVisibleRow = wtTable.getFirstVisibleRow();
  70. if (topOverlay.mainTableScrollableElement === window) {
  71. const rootElementOffset = offset(wtTable.wtRootElement);
  72. const totalTableHeight = innerHeight(wtTable.hider);
  73. const windowHeight = innerHeight(window);
  74. const windowScrollTop = getScrollTop(window);
  75. // Only calculate firstVisibleRow when table didn't filled (from up) whole viewport space
  76. if (rootElementOffset.top + totalTableHeight - windowHeight <= windowScrollTop) {
  77. let rowsHeight = wtViewport.getColumnHeaderHeight();
  78. rowsHeight += topOverlay.sumCellSizes(0, fixedRowsTop);
  79. rangeEachReverse(totalRows, 1, (row) => {
  80. rowsHeight += topOverlay.sumCellSizes(row - 1, row);
  81. if (rootElementOffset.top + totalTableHeight - rowsHeight <= windowScrollTop) {
  82. // Return physical row + 1
  83. firstVisibleRow = row;
  84. return false;
  85. }
  86. });
  87. }
  88. }
  89. return firstVisibleRow;
  90. }
  91. /**
  92. * Get last visible row based on virtual dom and how table is visible in browser window viewport.
  93. *
  94. * @returns {Number}
  95. */
  96. getLastVisibleRow() {
  97. const {
  98. topOverlay,
  99. wtTable,
  100. wtViewport,
  101. totalRows,
  102. } = this._getVariables();
  103. let lastVisibleRow = wtTable.getLastVisibleRow();
  104. if (topOverlay.mainTableScrollableElement === window) {
  105. const rootElementOffset = offset(wtTable.wtRootElement);
  106. const windowHeight = innerHeight(window);
  107. const windowScrollTop = getScrollTop(window);
  108. // Only calculate lastVisibleRow when table didn't filled (from bottom) whole viewport space
  109. if (rootElementOffset.top > windowScrollTop) {
  110. let rowsHeight = wtViewport.getColumnHeaderHeight();
  111. rangeEach(1, totalRows, (row) => {
  112. rowsHeight += topOverlay.sumCellSizes(row - 1, row);
  113. if (rootElementOffset.top + rowsHeight - windowScrollTop >= windowHeight) {
  114. // Return physical row - 1 (-2 because rangeEach gives row index + 1 - sumCellSizes requirements)
  115. lastVisibleRow = row - 2;
  116. return false;
  117. }
  118. });
  119. }
  120. }
  121. return lastVisibleRow;
  122. }
  123. /**
  124. * Get first visible column based on virtual dom and how table is visible in browser window viewport.
  125. *
  126. * @returns {Number}
  127. */
  128. getFirstVisibleColumn() {
  129. const {
  130. leftOverlay,
  131. wtTable,
  132. wtViewport,
  133. totalColumns,
  134. fixedColumnsLeft,
  135. } = this._getVariables();
  136. let firstVisibleColumn = wtTable.getFirstVisibleColumn();
  137. if (leftOverlay.mainTableScrollableElement === window) {
  138. const rootElementOffset = offset(wtTable.wtRootElement);
  139. const totalTableWidth = innerWidth(wtTable.hider);
  140. const windowWidth = innerWidth(window);
  141. const windowScrollLeft = getScrollLeft(window);
  142. // Only calculate firstVisibleColumn when table didn't filled (from left) whole viewport space
  143. if (rootElementOffset.left + totalTableWidth - windowWidth <= windowScrollLeft) {
  144. let columnsWidth = wtViewport.getRowHeaderWidth();
  145. rangeEachReverse(totalColumns, 1, (column) => {
  146. columnsWidth += leftOverlay.sumCellSizes(column - 1, column);
  147. if (rootElementOffset.left + totalTableWidth - columnsWidth <= windowScrollLeft) {
  148. // Return physical column + 1
  149. firstVisibleColumn = column;
  150. return false;
  151. }
  152. });
  153. }
  154. }
  155. return firstVisibleColumn;
  156. }
  157. /**
  158. * Get last visible column based on virtual dom and how table is visible in browser window viewport.
  159. *
  160. * @returns {Number}
  161. */
  162. getLastVisibleColumn() {
  163. const {
  164. leftOverlay,
  165. wtTable,
  166. wtViewport,
  167. totalColumns,
  168. } = this._getVariables();
  169. let lastVisibleColumn = wtTable.getLastVisibleColumn();
  170. if (leftOverlay.mainTableScrollableElement === window) {
  171. const rootElementOffset = offset(wtTable.wtRootElement);
  172. const windowWidth = innerWidth(window);
  173. const windowScrollLeft = getScrollLeft(window);
  174. // Only calculate lastVisibleColumn when table didn't filled (from right) whole viewport space
  175. if (rootElementOffset.left > windowScrollLeft) {
  176. let columnsWidth = wtViewport.getRowHeaderWidth();
  177. rangeEach(1, totalColumns, (column) => {
  178. columnsWidth += leftOverlay.sumCellSizes(column - 1, column);
  179. if (rootElementOffset.left + columnsWidth - windowScrollLeft >= windowWidth) {
  180. // Return physical column - 1 (-2 because rangeEach gives column index + 1 - sumCellSizes requirements)
  181. lastVisibleColumn = column - 2;
  182. return false;
  183. }
  184. });
  185. }
  186. }
  187. return lastVisibleColumn;
  188. }
  189. /**
  190. * Returns collection of variables used to rows and columns visibility calculations.
  191. *
  192. * @returns {Object}
  193. * @private
  194. */
  195. _getVariables() {
  196. const wot = this.wot;
  197. const topOverlay = wot.wtOverlays.topOverlay;
  198. const leftOverlay = wot.wtOverlays.leftOverlay;
  199. const wtTable = wot.wtTable;
  200. const wtViewport = wot.wtViewport;
  201. const totalRows = wot.getSetting('totalRows');
  202. const totalColumns = wot.getSetting('totalColumns');
  203. const fixedRowsTop = wot.getSetting('fixedRowsTop');
  204. const fixedRowsBottom = wot.getSetting('fixedRowsBottom');
  205. const fixedColumnsLeft = wot.getSetting('fixedColumnsLeft');
  206. return {
  207. topOverlay,
  208. leftOverlay,
  209. wtTable,
  210. wtViewport,
  211. totalRows,
  212. totalColumns,
  213. fixedRowsTop,
  214. fixedRowsBottom,
  215. fixedColumnsLeft
  216. };
  217. }
  218. }
  219. export default Scroll;