manualColumnResize.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. 'use strict';
  2. exports.__esModule = true;
  3. var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
  4. var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } };
  5. var _base = require('./../_base.js');
  6. var _base2 = _interopRequireDefault(_base);
  7. var _element = require('./../../helpers/dom/element');
  8. var _eventManager = require('./../../eventManager');
  9. var _eventManager2 = _interopRequireDefault(_eventManager);
  10. var _event = require('./../../helpers/dom/event');
  11. var _array = require('./../../helpers/array');
  12. var _number = require('./../../helpers/number');
  13. var _plugins = require('./../../plugins');
  14. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  15. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  16. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  17. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  18. // Developer note! Whenever you make a change in this file, make an analogous change in manualRowResize.js
  19. /**
  20. * @description
  21. * ManualColumnResize Plugin.
  22. *
  23. * Has 2 UI components:
  24. * - handle - the draggable element that sets the desired width of the column.
  25. * - guide - the helper guide that shows the desired width as a vertical guide.
  26. *
  27. * @plugin ManualColumnResize
  28. */
  29. var ManualColumnResize = function (_BasePlugin) {
  30. _inherits(ManualColumnResize, _BasePlugin);
  31. function ManualColumnResize(hotInstance) {
  32. _classCallCheck(this, ManualColumnResize);
  33. var _this = _possibleConstructorReturn(this, (ManualColumnResize.__proto__ || Object.getPrototypeOf(ManualColumnResize)).call(this, hotInstance));
  34. _this.currentTH = null;
  35. _this.currentCol = null;
  36. _this.selectedCols = [];
  37. _this.currentWidth = null;
  38. _this.newSize = null;
  39. _this.startY = null;
  40. _this.startWidth = null;
  41. _this.startOffset = null;
  42. _this.handle = document.createElement('DIV');
  43. _this.guide = document.createElement('DIV');
  44. _this.eventManager = new _eventManager2.default(_this);
  45. _this.pressed = null;
  46. _this.dblclick = 0;
  47. _this.autoresizeTimeout = null;
  48. _this.manualColumnWidths = [];
  49. (0, _element.addClass)(_this.handle, 'manualColumnResizer');
  50. (0, _element.addClass)(_this.guide, 'manualColumnResizerGuide');
  51. return _this;
  52. }
  53. /**
  54. * Check if the plugin is enabled in the handsontable settings.
  55. *
  56. * @returns {Boolean}
  57. */
  58. _createClass(ManualColumnResize, [{
  59. key: 'isEnabled',
  60. value: function isEnabled() {
  61. return this.hot.getSettings().manualColumnResize;
  62. }
  63. /**
  64. * Enable plugin for this Handsontable instance.
  65. */
  66. }, {
  67. key: 'enablePlugin',
  68. value: function enablePlugin() {
  69. var _this2 = this;
  70. if (this.enabled) {
  71. return;
  72. }
  73. this.manualColumnWidths = [];
  74. var initialColumnWidth = this.hot.getSettings().manualColumnResize;
  75. var loadedManualColumnWidths = this.loadManualColumnWidths();
  76. this.addHook('modifyColWidth', function (width, col) {
  77. return _this2.onModifyColWidth(width, col);
  78. });
  79. this.addHook('beforeStretchingColumnWidth', function (stretchedWidth, column) {
  80. return _this2.onBeforeStretchingColumnWidth(stretchedWidth, column);
  81. });
  82. this.addHook('beforeColumnResize', function (currentColumn, newSize, isDoubleClick) {
  83. return _this2.onBeforeColumnResize(currentColumn, newSize, isDoubleClick);
  84. });
  85. if (typeof loadedManualColumnWidths != 'undefined') {
  86. this.manualColumnWidths = loadedManualColumnWidths;
  87. } else if (Array.isArray(initialColumnWidth)) {
  88. this.manualColumnWidths = initialColumnWidth;
  89. } else {
  90. this.manualColumnWidths = [];
  91. }
  92. // Handsontable.hooks.register('beforeColumnResize');
  93. // Handsontable.hooks.register('afterColumnResize');
  94. this.bindEvents();
  95. _get(ManualColumnResize.prototype.__proto__ || Object.getPrototypeOf(ManualColumnResize.prototype), 'enablePlugin', this).call(this);
  96. }
  97. /**
  98. * Updates the plugin to use the latest options you have specified.
  99. */
  100. }, {
  101. key: 'updatePlugin',
  102. value: function updatePlugin() {
  103. var initialColumnWidth = this.hot.getSettings().manualColumnResize;
  104. if (Array.isArray(initialColumnWidth)) {
  105. this.manualColumnWidths = initialColumnWidth;
  106. } else if (!initialColumnWidth) {
  107. this.manualColumnWidths = [];
  108. }
  109. }
  110. /**
  111. * Disable plugin for this Handsontable instance.
  112. */
  113. }, {
  114. key: 'disablePlugin',
  115. value: function disablePlugin() {
  116. _get(ManualColumnResize.prototype.__proto__ || Object.getPrototypeOf(ManualColumnResize.prototype), 'disablePlugin', this).call(this);
  117. }
  118. /**
  119. * Save the current sizes using the persistentState plugin.
  120. */
  121. }, {
  122. key: 'saveManualColumnWidths',
  123. value: function saveManualColumnWidths() {
  124. this.hot.runHooks('persistentStateSave', 'manualColumnWidths', this.manualColumnWidths);
  125. }
  126. /**
  127. * Load the previously saved sizes using the persistentState plugin.
  128. *
  129. * @returns {Array}
  130. */
  131. }, {
  132. key: 'loadManualColumnWidths',
  133. value: function loadManualColumnWidths() {
  134. var storedState = {};
  135. this.hot.runHooks('persistentStateLoad', 'manualColumnWidths', storedState);
  136. return storedState.value;
  137. }
  138. /**
  139. * Set the resize handle position.
  140. *
  141. * @param {HTMLCellElement} TH TH HTML element.
  142. */
  143. }, {
  144. key: 'setupHandlePosition',
  145. value: function setupHandlePosition(TH) {
  146. var _this3 = this;
  147. if (!TH.parentNode) {
  148. return false;
  149. }
  150. this.currentTH = TH;
  151. var col = this.hot.view.wt.wtTable.getCoords(TH).col; // getCoords returns CellCoords
  152. var headerHeight = (0, _element.outerHeight)(this.currentTH);
  153. if (col >= 0) {
  154. // if not col header
  155. var box = this.currentTH.getBoundingClientRect();
  156. this.currentCol = col;
  157. this.selectedCols = [];
  158. if (this.hot.selection.isSelected() && this.hot.selection.selectedHeader.cols) {
  159. var _hot$getSelectedRange = this.hot.getSelectedRange(),
  160. from = _hot$getSelectedRange.from,
  161. to = _hot$getSelectedRange.to;
  162. var start = from.col;
  163. var end = to.col;
  164. if (start >= end) {
  165. start = to.col;
  166. end = from.col;
  167. }
  168. if (this.currentCol >= start && this.currentCol <= end) {
  169. (0, _number.rangeEach)(start, end, function (i) {
  170. return _this3.selectedCols.push(i);
  171. });
  172. } else {
  173. this.selectedCols.push(this.currentCol);
  174. }
  175. } else {
  176. this.selectedCols.push(this.currentCol);
  177. }
  178. this.startOffset = box.left - 6;
  179. this.startWidth = parseInt(box.width, 10);
  180. this.handle.style.top = box.top + 'px';
  181. this.handle.style.left = this.startOffset + this.startWidth + 'px';
  182. this.handle.style.height = headerHeight + 'px';
  183. this.hot.rootElement.appendChild(this.handle);
  184. }
  185. }
  186. /**
  187. * Refresh the resize handle position.
  188. */
  189. }, {
  190. key: 'refreshHandlePosition',
  191. value: function refreshHandlePosition() {
  192. this.handle.style.left = this.startOffset + this.currentWidth + 'px';
  193. }
  194. /**
  195. * Set the resize guide position.
  196. */
  197. }, {
  198. key: 'setupGuidePosition',
  199. value: function setupGuidePosition() {
  200. var handleHeight = parseInt((0, _element.outerHeight)(this.handle), 10);
  201. var handleBottomPosition = parseInt(this.handle.style.top, 10) + handleHeight;
  202. var maximumVisibleElementHeight = parseInt(this.hot.view.maximumVisibleElementHeight(0), 10);
  203. (0, _element.addClass)(this.handle, 'active');
  204. (0, _element.addClass)(this.guide, 'active');
  205. this.guide.style.top = handleBottomPosition + 'px';
  206. this.guide.style.left = this.handle.style.left;
  207. this.guide.style.height = maximumVisibleElementHeight - handleHeight + 'px';
  208. this.hot.rootElement.appendChild(this.guide);
  209. }
  210. /**
  211. * Refresh the resize guide position.
  212. */
  213. }, {
  214. key: 'refreshGuidePosition',
  215. value: function refreshGuidePosition() {
  216. this.guide.style.left = this.handle.style.left;
  217. }
  218. /**
  219. * Hide both the resize handle and resize guide.
  220. */
  221. }, {
  222. key: 'hideHandleAndGuide',
  223. value: function hideHandleAndGuide() {
  224. (0, _element.removeClass)(this.handle, 'active');
  225. (0, _element.removeClass)(this.guide, 'active');
  226. }
  227. /**
  228. * Check if provided element is considered a column header.
  229. *
  230. * @param {HTMLElement} element HTML element.
  231. * @returns {Boolean}
  232. */
  233. }, {
  234. key: 'checkIfColumnHeader',
  235. value: function checkIfColumnHeader(element) {
  236. if (element != this.hot.rootElement) {
  237. var parent = element.parentNode;
  238. if (parent.tagName === 'THEAD') {
  239. return true;
  240. }
  241. return this.checkIfColumnHeader(parent);
  242. }
  243. return false;
  244. }
  245. /**
  246. * Get the TH element from the provided element.
  247. *
  248. * @param {HTMLElement} element HTML element.
  249. * @returns {HTMLElement}
  250. */
  251. }, {
  252. key: 'getTHFromTargetElement',
  253. value: function getTHFromTargetElement(element) {
  254. if (element.tagName != 'TABLE') {
  255. if (element.tagName == 'TH') {
  256. return element;
  257. }
  258. return this.getTHFromTargetElement(element.parentNode);
  259. }
  260. return null;
  261. }
  262. /**
  263. * 'mouseover' event callback - set the handle position.
  264. *
  265. * @private
  266. * @param {MouseEvent} event
  267. */
  268. }, {
  269. key: 'onMouseOver',
  270. value: function onMouseOver(event) {
  271. if (this.checkIfColumnHeader(event.target)) {
  272. var th = this.getTHFromTargetElement(event.target);
  273. if (!th) {
  274. return;
  275. }
  276. var colspan = th.getAttribute('colspan');
  277. if (th && (colspan === null || colspan === 1)) {
  278. if (!this.pressed) {
  279. this.setupHandlePosition(th);
  280. }
  281. }
  282. }
  283. }
  284. /**
  285. * Auto-size row after doubleclick - callback.
  286. *
  287. * @private
  288. */
  289. }, {
  290. key: 'afterMouseDownTimeout',
  291. value: function afterMouseDownTimeout() {
  292. var _this4 = this;
  293. var render = function render() {
  294. _this4.hot.forceFullRender = true;
  295. _this4.hot.view.render(); // updates all
  296. _this4.hot.view.wt.wtOverlays.adjustElementsSize(true);
  297. };
  298. var resize = function resize(selectedCol, forceRender) {
  299. var hookNewSize = _this4.hot.runHooks('beforeColumnResize', selectedCol, _this4.newSize, true);
  300. if (hookNewSize !== void 0) {
  301. _this4.newSize = hookNewSize;
  302. }
  303. if (_this4.hot.getSettings().stretchH === 'all') {
  304. _this4.clearManualSize(selectedCol);
  305. } else {
  306. _this4.setManualSize(selectedCol, _this4.newSize); // double click sets by auto row size plugin
  307. }
  308. if (forceRender) {
  309. render();
  310. }
  311. _this4.saveManualColumnWidths();
  312. _this4.hot.runHooks('afterColumnResize', selectedCol, _this4.newSize, true);
  313. };
  314. if (this.dblclick >= 2) {
  315. var selectedColsLength = this.selectedCols.length;
  316. if (selectedColsLength > 1) {
  317. (0, _array.arrayEach)(this.selectedCols, function (selectedCol) {
  318. resize(selectedCol);
  319. });
  320. render();
  321. } else {
  322. (0, _array.arrayEach)(this.selectedCols, function (selectedCol) {
  323. resize(selectedCol, true);
  324. });
  325. }
  326. }
  327. this.dblclick = 0;
  328. this.autoresizeTimeout = null;
  329. }
  330. /**
  331. * 'mousedown' event callback.
  332. *
  333. * @private
  334. * @param {MouseEvent} e
  335. */
  336. }, {
  337. key: 'onMouseDown',
  338. value: function onMouseDown(event) {
  339. var _this5 = this;
  340. if ((0, _element.hasClass)(event.target, 'manualColumnResizer')) {
  341. this.setupGuidePosition();
  342. this.pressed = this.hot;
  343. if (this.autoresizeTimeout === null) {
  344. this.autoresizeTimeout = setTimeout(function () {
  345. return _this5.afterMouseDownTimeout();
  346. }, 500);
  347. this.hot._registerTimeout(this.autoresizeTimeout);
  348. }
  349. this.dblclick++;
  350. this.startX = (0, _event.pageX)(event);
  351. this.newSize = this.startWidth;
  352. }
  353. }
  354. /**
  355. * 'mousemove' event callback - refresh the handle and guide positions, cache the new column width.
  356. *
  357. * @private
  358. * @param {MouseEvent} e
  359. */
  360. }, {
  361. key: 'onMouseMove',
  362. value: function onMouseMove(event) {
  363. var _this6 = this;
  364. if (this.pressed) {
  365. this.currentWidth = this.startWidth + ((0, _event.pageX)(event) - this.startX);
  366. (0, _array.arrayEach)(this.selectedCols, function (selectedCol) {
  367. _this6.newSize = _this6.setManualSize(selectedCol, _this6.currentWidth);
  368. });
  369. this.refreshHandlePosition();
  370. this.refreshGuidePosition();
  371. }
  372. }
  373. /**
  374. * 'mouseup' event callback - apply the column resizing.
  375. *
  376. * @private
  377. * @param {MouseEvent} e
  378. */
  379. }, {
  380. key: 'onMouseUp',
  381. value: function onMouseUp(event) {
  382. var _this7 = this;
  383. var render = function render() {
  384. _this7.hot.forceFullRender = true;
  385. _this7.hot.view.render(); // updates all
  386. _this7.hot.view.wt.wtOverlays.adjustElementsSize(true);
  387. };
  388. var resize = function resize(selectedCol, forceRender) {
  389. _this7.hot.runHooks('beforeColumnResize', selectedCol, _this7.newSize);
  390. if (forceRender) {
  391. render();
  392. }
  393. _this7.saveManualColumnWidths();
  394. _this7.hot.runHooks('afterColumnResize', selectedCol, _this7.newSize);
  395. };
  396. if (this.pressed) {
  397. this.hideHandleAndGuide();
  398. this.pressed = false;
  399. if (this.newSize != this.startWidth) {
  400. var selectedColsLength = this.selectedCols.length;
  401. if (selectedColsLength > 1) {
  402. (0, _array.arrayEach)(this.selectedCols, function (selectedCol) {
  403. resize(selectedCol);
  404. });
  405. render();
  406. } else {
  407. (0, _array.arrayEach)(this.selectedCols, function (selectedCol) {
  408. resize(selectedCol, true);
  409. });
  410. }
  411. }
  412. this.setupHandlePosition(this.currentTH);
  413. }
  414. }
  415. /**
  416. * Bind the mouse events.
  417. *
  418. * @private
  419. */
  420. }, {
  421. key: 'bindEvents',
  422. value: function bindEvents() {
  423. var _this8 = this;
  424. this.eventManager.addEventListener(this.hot.rootElement, 'mouseover', function (e) {
  425. return _this8.onMouseOver(e);
  426. });
  427. this.eventManager.addEventListener(this.hot.rootElement, 'mousedown', function (e) {
  428. return _this8.onMouseDown(e);
  429. });
  430. this.eventManager.addEventListener(window, 'mousemove', function (e) {
  431. return _this8.onMouseMove(e);
  432. });
  433. this.eventManager.addEventListener(window, 'mouseup', function (e) {
  434. return _this8.onMouseUp(e);
  435. });
  436. }
  437. /**
  438. * Cache the current column width.
  439. *
  440. * @param {Number} column Column index.
  441. * @param {Number} width Column width.
  442. * @returns {Number}
  443. */
  444. }, {
  445. key: 'setManualSize',
  446. value: function setManualSize(column, width) {
  447. width = Math.max(width, 20);
  448. /**
  449. * We need to run col through modifyCol hook, in case the order of displayed columns is different than the order
  450. * in data source. For instance, this order can be modified by manualColumnMove plugin.
  451. */
  452. column = this.hot.runHooks('modifyCol', column);
  453. this.manualColumnWidths[column] = width;
  454. return width;
  455. }
  456. /**
  457. * Clear cache for the current column index.
  458. *
  459. * @param {Number} column Column index.
  460. */
  461. }, {
  462. key: 'clearManualSize',
  463. value: function clearManualSize(column) {
  464. column = this.hot.runHooks('modifyCol', column);
  465. this.manualColumnWidths[column] = void 0;
  466. }
  467. /**
  468. * Modify the provided column width, based on the plugin settings
  469. *
  470. * @private
  471. * @param {Number} width Column width.
  472. * @param {Number} column Column index.
  473. * @returns {Number}
  474. */
  475. }, {
  476. key: 'onModifyColWidth',
  477. value: function onModifyColWidth(width, column) {
  478. if (this.enabled) {
  479. column = this.hot.runHooks('modifyCol', column);
  480. if (this.hot.getSettings().manualColumnResize && this.manualColumnWidths[column]) {
  481. return this.manualColumnWidths[column];
  482. }
  483. }
  484. return width;
  485. }
  486. /**
  487. * Modify the provided column stretched width. This hook decides if specified column should be stretched or not.
  488. *
  489. * @private
  490. * @param {Number} stretchedWidth Stretched width.
  491. * @param {Number} column Column index.
  492. * @returns {Number}
  493. */
  494. }, {
  495. key: 'onBeforeStretchingColumnWidth',
  496. value: function onBeforeStretchingColumnWidth(stretchedWidth, column) {
  497. var width = this.manualColumnWidths[column];
  498. if (width === void 0) {
  499. width = stretchedWidth;
  500. }
  501. return width;
  502. }
  503. /**
  504. * `beforeColumnResize` hook callback.
  505. *
  506. * @private
  507. * @param {Number} currentColumn Index of the resized column.
  508. * @param {Number} newSize Calculated new column width.
  509. * @param {Boolean} isDoubleClick Flag that determines whether there was a double-click.
  510. */
  511. }, {
  512. key: 'onBeforeColumnResize',
  513. value: function onBeforeColumnResize() {
  514. // clear the header height cache information
  515. this.hot.view.wt.wtViewport.hasOversizedColumnHeadersMarked = {};
  516. }
  517. }]);
  518. return ManualColumnResize;
  519. }(_base2.default);
  520. (0, _plugins.registerPlugin)('manualColumnResize', ManualColumnResize);
  521. exports.default = ManualColumnResize;