comments.js 25 KB

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