comments.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903
  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 _element = require('./../../helpers/dom/element');
  6. var _object = require('./../../helpers/object');
  7. var _function = require('./../../helpers/function');
  8. var _eventManager = require('./../../eventManager');
  9. var _eventManager2 = _interopRequireDefault(_eventManager);
  10. var _src = require('./../../3rdparty/walkontable/src');
  11. var _plugins = require('./../../plugins');
  12. var _base = require('./../_base');
  13. var _base2 = _interopRequireDefault(_base);
  14. var _commentEditor = require('./commentEditor');
  15. var _commentEditor2 = _interopRequireDefault(_commentEditor);
  16. var _utils = require('./../contextMenu/utils');
  17. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  18. function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
  19. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  20. 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; }
  21. 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; }
  22. var privatePool = new WeakMap();
  23. var META_COMMENT = 'comment';
  24. var META_COMMENT_VALUE = 'value';
  25. var META_STYLE = 'style';
  26. var META_READONLY = 'readOnly';
  27. /**
  28. * @plugin Comments
  29. *
  30. * @description
  31. * This plugin allows setting and managing cell comments by either an option in the context menu or with the use of the API.
  32. *
  33. * To enable the plugin, you'll need to set the comments property of the config object to `true`:
  34. * ```js
  35. * ...
  36. * comments: true
  37. * ...
  38. * ```
  39. *
  40. * To add comments at the table initialization, define the `comment` property in the `cell` config array as in an example below.
  41. *
  42. * @example
  43. *
  44. * ```js
  45. * ...
  46. * var hot = new Handsontable(document.getElementById('example'), {
  47. * date: getData(),
  48. * comments: true,
  49. * cell: [
  50. * {row: 1, col: 1, comment: {value: 'Foo'}},
  51. * {row: 2, col: 2, comment: {value: 'Bar'}}
  52. * ]
  53. * });
  54. *
  55. * // Access to the Comments plugin instance:
  56. * var commentsPlugin = hot.getPlugin('comments');
  57. *
  58. * // Manage comments programmatically:
  59. * commentsPlugin.editor.setCommentAtCell(1, 6, 'Comment contents');
  60. * commentsPlugin.showAtCell(1, 6);
  61. * commentsPlugin.removeCommentAtCell(1, 6);
  62. *
  63. * // You can also set range once and use proper methods:
  64. * commentsPlugin.setRange({row: 1, col: 6});
  65. * commentsPlugin.setComment('Comment contents');
  66. * commentsPlugin.show();
  67. * commentsPlugin.removeComment();
  68. * ...
  69. * ```
  70. */
  71. var Comments = function (_BasePlugin) {
  72. _inherits(Comments, _BasePlugin);
  73. function Comments(hotInstance) {
  74. _classCallCheck(this, Comments);
  75. /**
  76. * Instance of {@link CommentEditor}.
  77. *
  78. * @type {CommentEditor}
  79. */
  80. var _this = _possibleConstructorReturn(this, (Comments.__proto__ || Object.getPrototypeOf(Comments)).call(this, hotInstance));
  81. _this.editor = null;
  82. /**
  83. * Instance of {@link EventManager}.
  84. *
  85. * @private
  86. * @type {EventManager}
  87. */
  88. _this.eventManager = null;
  89. /**
  90. * Current cell range.
  91. *
  92. * @type {Object}
  93. */
  94. _this.range = {};
  95. /**
  96. * @private
  97. * @type {Boolean}
  98. */
  99. _this.mouseDown = false;
  100. /**
  101. * @private
  102. * @type {Boolean}
  103. */
  104. _this.contextMenuEvent = false;
  105. /**
  106. * @private
  107. * @type {*}
  108. */
  109. _this.timer = null;
  110. /**
  111. * Delay used when showing/hiding the comments (in milliseconds).
  112. *
  113. * @type {Number}
  114. */
  115. _this.displayDelay = 250;
  116. privatePool.set(_this, {
  117. tempEditorDimensions: {},
  118. cellBelowCursor: null
  119. });
  120. return _this;
  121. }
  122. /**
  123. * Check if the plugin is enabled in the Handsontable settings.
  124. *
  125. * @returns {Boolean}
  126. */
  127. _createClass(Comments, [{
  128. key: 'isEnabled',
  129. value: function isEnabled() {
  130. return !!this.hot.getSettings().comments;
  131. }
  132. /**
  133. * Enable plugin for this Handsontable instance.
  134. */
  135. }, {
  136. key: 'enablePlugin',
  137. value: function enablePlugin() {
  138. var _this2 = this;
  139. if (this.enabled) {
  140. return;
  141. }
  142. if (!this.editor) {
  143. this.editor = new _commentEditor2.default();
  144. }
  145. if (!this.eventManager) {
  146. this.eventManager = new _eventManager2.default(this);
  147. }
  148. this.addHook('afterContextMenuDefaultOptions', function (options) {
  149. return _this2.addToContextMenu(options);
  150. });
  151. this.addHook('afterRenderer', function (TD, row, col, prop, value, cellProperties) {
  152. return _this2.onAfterRenderer(TD, cellProperties);
  153. });
  154. this.addHook('afterScrollHorizontally', function () {
  155. return _this2.hide();
  156. });
  157. this.addHook('afterScrollVertically', function () {
  158. return _this2.hide();
  159. });
  160. this.addHook('afterBeginEditing', function (args) {
  161. return _this2.onAfterBeginEditing(args);
  162. });
  163. this.registerListeners();
  164. _get(Comments.prototype.__proto__ || Object.getPrototypeOf(Comments.prototype), 'enablePlugin', this).call(this);
  165. }
  166. /**
  167. * Disable plugin for this Handsontable instance.
  168. */
  169. }, {
  170. key: 'disablePlugin',
  171. value: function disablePlugin() {
  172. _get(Comments.prototype.__proto__ || Object.getPrototypeOf(Comments.prototype), 'disablePlugin', this).call(this);
  173. }
  174. /**
  175. * Register all necessary DOM listeners.
  176. *
  177. * @private
  178. */
  179. }, {
  180. key: 'registerListeners',
  181. value: function registerListeners() {
  182. var _this3 = this;
  183. this.eventManager.addEventListener(document, 'mouseover', function (event) {
  184. return _this3.onMouseOver(event);
  185. });
  186. this.eventManager.addEventListener(document, 'mousedown', function (event) {
  187. return _this3.onMouseDown(event);
  188. });
  189. this.eventManager.addEventListener(document, 'mouseup', function (event) {
  190. return _this3.onMouseUp(event);
  191. });
  192. this.eventManager.addEventListener(this.editor.getInputElement(), 'blur', function (event) {
  193. return _this3.onEditorBlur(event);
  194. });
  195. this.eventManager.addEventListener(this.editor.getInputElement(), 'mousedown', function (event) {
  196. return _this3.onEditorMouseDown(event);
  197. });
  198. this.eventManager.addEventListener(this.editor.getInputElement(), 'mouseup', function (event) {
  199. return _this3.onEditorMouseUp(event);
  200. });
  201. }
  202. /**
  203. * Set current cell range to be able to use general methods like {@link Comments#setComment},
  204. * {@link Comments#removeComment}, {@link Comments#show}.
  205. *
  206. * @param {Object} range Object with `from` and `to` properties, each with `row` and `col` properties.
  207. */
  208. }, {
  209. key: 'setRange',
  210. value: function setRange(range) {
  211. this.range = range;
  212. }
  213. /**
  214. * Clear the currently selected cell.
  215. */
  216. }, {
  217. key: 'clearRange',
  218. value: function clearRange() {
  219. this.range = {};
  220. }
  221. /**
  222. * Check if the event target is a cell containing a comment.
  223. *
  224. * @param {Event} event DOM event
  225. * @returns {Boolean}
  226. */
  227. }, {
  228. key: 'targetIsCellWithComment',
  229. value: function targetIsCellWithComment(event) {
  230. var closestCell = (0, _element.closest)(event.target, 'TD', 'TBODY');
  231. return !!(closestCell && (0, _element.hasClass)(closestCell, 'htCommentCell') && (0, _element.closest)(closestCell, [this.hot.rootElement]));
  232. }
  233. /**
  234. * Check if the event target is a comment textarea.
  235. *
  236. * @param {Event} event DOM event.
  237. * @returns {Boolean}
  238. */
  239. }, {
  240. key: 'targetIsCommentTextArea',
  241. value: function targetIsCommentTextArea(event) {
  242. return this.editor.getInputElement() === event.target;
  243. }
  244. /**
  245. * Set a comment for a cell according to the previously set range (see {@link Comments#setRange}).
  246. *
  247. * @param {String} value Comment contents.
  248. */
  249. }, {
  250. key: 'setComment',
  251. value: function setComment(value) {
  252. if (!this.range.from) {
  253. throw new Error('Before using this method, first set cell range (hot.getPlugin("comment").setRange())');
  254. }
  255. var editorValue = this.editor.getValue();
  256. var comment = '';
  257. if (value != null) {
  258. comment = value;
  259. } else if (editorValue != null) {
  260. comment = editorValue;
  261. }
  262. var row = this.range.from.row;
  263. var col = this.range.from.col;
  264. this.updateCommentMeta(row, col, _defineProperty({}, META_COMMENT_VALUE, comment));
  265. this.hot.render();
  266. }
  267. /**
  268. * Set a comment for a cell.
  269. *
  270. * @param {Number} row Row index.
  271. * @param {Number} col Column index.
  272. * @param {String} value Comment contents.
  273. */
  274. }, {
  275. key: 'setCommentAtCell',
  276. value: function setCommentAtCell(row, col, value) {
  277. this.setRange({
  278. from: new _src.CellCoords(row, col)
  279. });
  280. this.setComment(value);
  281. }
  282. /**
  283. * Remove a comment from a cell according to previously set range (see {@link Comments#setRange}).
  284. *
  285. * @param {Boolean} [forceRender = true] If set to `true`, the table will be re-rendered at the end of the operation.
  286. */
  287. }, {
  288. key: 'removeComment',
  289. value: function removeComment() {
  290. var forceRender = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
  291. if (!this.range.from) {
  292. throw new Error('Before using this method, first set cell range (hot.getPlugin("comment").setRange())');
  293. }
  294. this.hot.setCellMeta(this.range.from.row, this.range.from.col, META_COMMENT, void 0);
  295. if (forceRender) {
  296. this.hot.render();
  297. }
  298. this.hide();
  299. }
  300. /**
  301. * Remove comment from a cell.
  302. *
  303. * @param {Number} row Row index.
  304. * @param {Number} col Column index.
  305. * @param {Boolean} [forceRender = true] If `true`, the table will be re-rendered at the end of the operation.
  306. */
  307. }, {
  308. key: 'removeCommentAtCell',
  309. value: function removeCommentAtCell(row, col) {
  310. var forceRender = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
  311. this.setRange({
  312. from: new _src.CellCoords(row, col)
  313. });
  314. this.removeComment(forceRender);
  315. }
  316. /**
  317. * Get comment from a cell at the predefined range.
  318. */
  319. }, {
  320. key: 'getComment',
  321. value: function getComment() {
  322. var row = this.range.from.row;
  323. var column = this.range.from.col;
  324. return this.getCommentMeta(row, column, META_COMMENT_VALUE);
  325. }
  326. /**
  327. * Get comment from a cell at the provided coordinates.
  328. *
  329. * @param {Number} row Row index.
  330. * @param {Number} column Column index.
  331. */
  332. }, {
  333. key: 'getCommentAtCell',
  334. value: function getCommentAtCell(row, column) {
  335. return this.getCommentMeta(row, column, META_COMMENT_VALUE);
  336. }
  337. /**
  338. * Show the comment editor accordingly to the previously set range (see {@link Comments#setRange}).
  339. *
  340. * @returns {Boolean} Returns `true` if comment editor was shown.
  341. */
  342. }, {
  343. key: 'show',
  344. value: function show() {
  345. if (!this.range.from) {
  346. throw new Error('Before using this method, first set cell range (hot.getPlugin("comment").setRange())');
  347. }
  348. var meta = this.hot.getCellMeta(this.range.from.row, this.range.from.col);
  349. this.refreshEditor(true);
  350. this.editor.setValue(meta[META_COMMENT] ? meta[META_COMMENT][META_COMMENT_VALUE] : null || '');
  351. if (this.editor.hidden) {
  352. this.editor.show();
  353. }
  354. return true;
  355. }
  356. /**
  357. * Show comment editor according to cell coordinates.
  358. *
  359. * @param {Number} row Row index.
  360. * @param {Number} col Column index.
  361. * @returns {Boolean} Returns `true` if comment editor was shown.
  362. */
  363. }, {
  364. key: 'showAtCell',
  365. value: function showAtCell(row, col) {
  366. this.setRange({
  367. from: new _src.CellCoords(row, col)
  368. });
  369. return this.show();
  370. }
  371. /**
  372. * Hide the comment editor.
  373. */
  374. }, {
  375. key: 'hide',
  376. value: function hide() {
  377. if (!this.editor.hidden) {
  378. this.editor.hide();
  379. }
  380. }
  381. /**
  382. * Refresh comment editor position and styling.
  383. *
  384. * @param {Boolean} [force=false] If `true` then recalculation will be forced.
  385. */
  386. }, {
  387. key: 'refreshEditor',
  388. value: function refreshEditor() {
  389. var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  390. if (!force && (!this.range.from || !this.editor.isVisible())) {
  391. return;
  392. }
  393. var scrollableElement = (0, _element.getScrollableElement)(this.hot.view.wt.wtTable.TABLE);
  394. var TD = this.hot.view.wt.wtTable.getCell(this.range.from);
  395. var row = this.range.from.row;
  396. var column = this.range.from.col;
  397. var cellOffset = (0, _element.offset)(TD);
  398. var lastColWidth = this.hot.view.wt.wtTable.getStretchedColumnWidth(column);
  399. var cellTopOffset = cellOffset.top < 0 ? 0 : cellOffset.top;
  400. var cellLeftOffset = cellOffset.left;
  401. if (this.hot.view.wt.wtViewport.hasVerticalScroll() && scrollableElement !== window) {
  402. cellTopOffset -= this.hot.view.wt.wtOverlays.topOverlay.getScrollPosition();
  403. }
  404. if (this.hot.view.wt.wtViewport.hasHorizontalScroll() && scrollableElement !== window) {
  405. cellLeftOffset -= this.hot.view.wt.wtOverlays.leftOverlay.getScrollPosition();
  406. }
  407. var x = cellLeftOffset + lastColWidth;
  408. var y = cellTopOffset;
  409. var commentStyle = this.getCommentMeta(row, column, META_STYLE);
  410. var readOnly = this.getCommentMeta(row, column, META_READONLY);
  411. if (commentStyle) {
  412. this.editor.setSize(commentStyle.width, commentStyle.height);
  413. } else {
  414. this.editor.resetSize();
  415. }
  416. this.editor.setReadOnlyState(readOnly);
  417. this.editor.setPosition(x, y);
  418. }
  419. /**
  420. * Check if there is a comment for selected range.
  421. *
  422. * @private
  423. * @returns {Boolean}
  424. */
  425. }, {
  426. key: 'checkSelectionCommentsConsistency',
  427. value: function checkSelectionCommentsConsistency() {
  428. var selected = this.hot.getSelectedRange();
  429. if (!selected) {
  430. return false;
  431. }
  432. var hasComment = false;
  433. var cell = selected.from; // IN EXCEL THERE IS COMMENT ONLY FOR TOP LEFT CELL IN SELECTION
  434. if (this.getCommentMeta(cell.row, cell.col, META_COMMENT_VALUE)) {
  435. hasComment = true;
  436. }
  437. return hasComment;
  438. }
  439. /**
  440. * Set or update the comment-related cell meta.
  441. *
  442. * @param {Number} row Row index.
  443. * @param {Number} column Column index.
  444. * @param {Object} metaObject Object defining all the comment-related meta information.
  445. */
  446. }, {
  447. key: 'updateCommentMeta',
  448. value: function updateCommentMeta(row, column, metaObject) {
  449. var oldComment = this.hot.getCellMeta(row, column)[META_COMMENT];
  450. var newComment = void 0;
  451. if (oldComment) {
  452. newComment = (0, _object.deepClone)(oldComment);
  453. (0, _object.deepExtend)(newComment, metaObject);
  454. } else {
  455. newComment = metaObject;
  456. }
  457. this.hot.setCellMeta(row, column, META_COMMENT, newComment);
  458. }
  459. /**
  460. * Get the comment related meta information.
  461. *
  462. * @param {Number} row Row index.
  463. * @param {Number} column Column index.
  464. * @param {String} property Cell meta property.
  465. * @returns {Mixed}
  466. */
  467. }, {
  468. key: 'getCommentMeta',
  469. value: function getCommentMeta(row, column, property) {
  470. var cellMeta = this.hot.getCellMeta(row, column);
  471. if (!cellMeta[META_COMMENT]) {
  472. return void 0;
  473. }
  474. return cellMeta[META_COMMENT][property];
  475. }
  476. /**
  477. * `mousedown` event callback.
  478. *
  479. * @private
  480. * @param {MouseEvent} event The `mousedown` event.
  481. */
  482. }, {
  483. key: 'onMouseDown',
  484. value: function onMouseDown(event) {
  485. this.mouseDown = true;
  486. if (!this.hot.view || !this.hot.view.wt) {
  487. return;
  488. }
  489. if (!this.contextMenuEvent && !this.targetIsCommentTextArea(event)) {
  490. var eventCell = (0, _element.closest)(event.target, 'TD', 'TBODY');
  491. var coordinates = null;
  492. if (eventCell) {
  493. coordinates = this.hot.view.wt.wtTable.getCoords(eventCell);
  494. }
  495. if (!eventCell || this.range.from && coordinates && (this.range.from.row !== coordinates.row || this.range.from.col !== coordinates.col)) {
  496. this.hide();
  497. }
  498. }
  499. this.contextMenuEvent = false;
  500. }
  501. /**
  502. * `mouseover` event callback.
  503. *
  504. * @private
  505. * @param {MouseEvent} event The `mouseover` event.
  506. */
  507. }, {
  508. key: 'onMouseOver',
  509. value: function onMouseOver(event) {
  510. var _this4 = this;
  511. if (this.mouseDown || this.editor.isFocused()) {
  512. return;
  513. }
  514. var priv = privatePool.get(this);
  515. priv.cellBelowCursor = document.elementFromPoint(event.clientX, event.clientY);
  516. (0, _function.debounce)(function () {
  517. if ((0, _element.hasClass)(event.target, 'wtBorder') || priv.cellBelowCursor !== event.target || !_this4.editor) {
  518. return;
  519. }
  520. if (_this4.targetIsCellWithComment(event)) {
  521. var coordinates = _this4.hot.view.wt.wtTable.getCoords(event.target);
  522. var range = {
  523. from: new _src.CellCoords(coordinates.row, coordinates.col)
  524. };
  525. _this4.setRange(range);
  526. _this4.show();
  527. } else if ((0, _element.isChildOf)(event.target, document) && !_this4.targetIsCommentTextArea(event) && !_this4.editor.isFocused()) {
  528. _this4.hide();
  529. }
  530. }, this.displayDelay)();
  531. }
  532. /**
  533. * `mouseup` event callback.
  534. *
  535. * @private
  536. * @param {MouseEvent} event The `mouseup` event.
  537. */
  538. }, {
  539. key: 'onMouseUp',
  540. value: function onMouseUp(event) {
  541. this.mouseDown = false;
  542. }
  543. /** *
  544. * The `afterRenderer` hook callback..
  545. *
  546. * @private
  547. * @param {HTMLTableCellElement} TD The rendered `TD` element.
  548. * @param {Object} cellProperties The rendered cell's property object.
  549. */
  550. }, {
  551. key: 'onAfterRenderer',
  552. value: function onAfterRenderer(TD, cellProperties) {
  553. if (cellProperties[META_COMMENT] && cellProperties[META_COMMENT][META_COMMENT_VALUE]) {
  554. (0, _element.addClass)(TD, cellProperties.commentedCellClassName);
  555. }
  556. }
  557. /**
  558. * `blur` event callback for the comment editor.
  559. *
  560. * @private
  561. * @param {Event} event The `blur` event.
  562. */
  563. }, {
  564. key: 'onEditorBlur',
  565. value: function onEditorBlur(event) {
  566. this.setComment();
  567. }
  568. /**
  569. * `mousedown` hook. Along with `onEditorMouseUp` used to simulate the textarea resizing event.
  570. *
  571. * @private
  572. * @param {MouseEvent} event The `mousedown` event.
  573. */
  574. }, {
  575. key: 'onEditorMouseDown',
  576. value: function onEditorMouseDown(event) {
  577. var priv = privatePool.get(this);
  578. priv.tempEditorDimensions = {
  579. width: (0, _element.outerWidth)(event.target),
  580. height: (0, _element.outerHeight)(event.target)
  581. };
  582. }
  583. /**
  584. * `mouseup` hook. Along with `onEditorMouseDown` used to simulate the textarea resizing event.
  585. *
  586. * @private
  587. * @param {MouseEvent} event The `mouseup` event.
  588. */
  589. }, {
  590. key: 'onEditorMouseUp',
  591. value: function onEditorMouseUp(event) {
  592. var priv = privatePool.get(this);
  593. var currentWidth = (0, _element.outerWidth)(event.target);
  594. var currentHeight = (0, _element.outerHeight)(event.target);
  595. if (currentWidth !== priv.tempEditorDimensions.width + 1 || currentHeight !== priv.tempEditorDimensions.height + 2) {
  596. this.updateCommentMeta(this.range.from.row, this.range.from.col, _defineProperty({}, META_STYLE, {
  597. width: currentWidth,
  598. height: currentHeight
  599. }));
  600. }
  601. }
  602. /**
  603. * Context Menu's "Add comment" callback. Results in showing the comment editor.
  604. *
  605. * @private
  606. */
  607. }, {
  608. key: 'onContextMenuAddComment',
  609. value: function onContextMenuAddComment() {
  610. var _this5 = this;
  611. var coords = this.hot.getSelectedRange();
  612. this.contextMenuEvent = true;
  613. this.setRange({
  614. from: coords.from
  615. });
  616. this.show();
  617. setTimeout(function () {
  618. if (_this5.hot) {
  619. _this5.hot.deselectCell();
  620. _this5.editor.focus();
  621. }
  622. }, 10);
  623. }
  624. /**
  625. * Context Menu's "remove comment" callback.
  626. *
  627. * @private
  628. * @param {Object} selection The current selection.
  629. */
  630. }, {
  631. key: 'onContextMenuRemoveComment',
  632. value: function onContextMenuRemoveComment(selection) {
  633. this.contextMenuEvent = true;
  634. for (var i = selection.start.row; i <= selection.end.row; i++) {
  635. for (var j = selection.start.col; j <= selection.end.col; j++) {
  636. this.removeCommentAtCell(i, j, false);
  637. }
  638. }
  639. this.hot.render();
  640. }
  641. /**
  642. * Context Menu's "make comment read-only" callback.
  643. *
  644. * @private
  645. * @param {Object} selection The current selection.
  646. */
  647. }, {
  648. key: 'onContextMenuMakeReadOnly',
  649. value: function onContextMenuMakeReadOnly(selection) {
  650. this.contextMenuEvent = true;
  651. for (var i = selection.start.row; i <= selection.end.row; i++) {
  652. for (var j = selection.start.col; j <= selection.end.col; j++) {
  653. var currentState = !!this.getCommentMeta(i, j, META_READONLY);
  654. this.updateCommentMeta(i, j, _defineProperty({}, META_READONLY, !currentState));
  655. }
  656. }
  657. }
  658. /**
  659. * Add Comments plugin options to the Context Menu.
  660. *
  661. * @private
  662. * @param {Object} defaultOptions
  663. */
  664. }, {
  665. key: 'addToContextMenu',
  666. value: function addToContextMenu(defaultOptions) {
  667. var _this6 = this;
  668. defaultOptions.items.push((0, _plugins.getPlugin)(this.hot, 'contextMenu').constructor.SEPARATOR, {
  669. key: 'commentsAddEdit',
  670. name: function name() {
  671. return _this6.checkSelectionCommentsConsistency() ? 'Edit comment' : 'Add comment';
  672. },
  673. callback: function callback() {
  674. return _this6.onContextMenuAddComment();
  675. },
  676. disabled: function disabled() {
  677. return !(this.getSelected() && !this.selection.selectedHeader.corner);
  678. }
  679. }, {
  680. key: 'commentsRemove',
  681. name: function name() {
  682. return 'Delete comment';
  683. },
  684. callback: function callback(key, selection) {
  685. return _this6.onContextMenuRemoveComment(selection);
  686. },
  687. disabled: function disabled() {
  688. return _this6.hot.selection.selectedHeader.corner;
  689. }
  690. }, {
  691. key: 'commentsReadOnly',
  692. name: function name() {
  693. var _this7 = this;
  694. var label = 'Read only comment';
  695. var hasProperty = (0, _utils.checkSelectionConsistency)(this.getSelectedRange(), function (row, col) {
  696. var readOnlyProperty = _this7.getCellMeta(row, col)[META_COMMENT];
  697. if (readOnlyProperty) {
  698. readOnlyProperty = readOnlyProperty[META_READONLY];
  699. }
  700. if (readOnlyProperty) {
  701. return true;
  702. }
  703. });
  704. if (hasProperty) {
  705. label = (0, _utils.markLabelAsSelected)(label);
  706. }
  707. return label;
  708. },
  709. callback: function callback(key, selection) {
  710. return _this6.onContextMenuMakeReadOnly(selection);
  711. },
  712. disabled: function disabled() {
  713. return _this6.hot.selection.selectedHeader.corner || !_this6.checkSelectionCommentsConsistency();
  714. }
  715. });
  716. }
  717. /**
  718. * `afterBeginEditing` hook callback.
  719. *
  720. * @private
  721. * @param {Number} row Row index of the currently edited cell.
  722. * @param {Number} column Column index of the currently edited cell.
  723. */
  724. }, {
  725. key: 'onAfterBeginEditing',
  726. value: function onAfterBeginEditing(row, column) {
  727. this.hide();
  728. }
  729. /**
  730. * Destroy plugin instance.
  731. */
  732. }, {
  733. key: 'destroy',
  734. value: function destroy() {
  735. if (this.editor) {
  736. this.editor.destroy();
  737. }
  738. _get(Comments.prototype.__proto__ || Object.getPrototypeOf(Comments.prototype), 'destroy', this).call(this);
  739. }
  740. }]);
  741. return Comments;
  742. }(_base2.default);
  743. (0, _plugins.registerPlugin)('comments', Comments);
  744. exports.default = Comments;