checklists.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. function initSorting(items) {
  2. items.sortable({
  3. tolerance: 'pointer',
  4. helper: 'clone',
  5. items: '.js-checklist-item:not(.placeholder)',
  6. axis: 'y',
  7. distance: 7,
  8. placeholder: 'placeholder',
  9. scroll: false,
  10. start(evt, ui) {
  11. ui.placeholder.height(ui.helper.height());
  12. EscapeActions.executeUpTo('popup-close');
  13. },
  14. stop(evt, ui) {
  15. const parent = ui.item.parents('.js-checklist-items');
  16. const orderedItems = [];
  17. parent.find('.js-checklist-item').each(function(i, item) {
  18. const checklistItem = Blaze.getData(item).item;
  19. orderedItems.push(checklistItem._id);
  20. });
  21. items.sortable('cancel');
  22. const formerParent = ui.item.parents('.js-checklist-items');
  23. let checklist = Blaze.getData(parent.get(0)).checklist;
  24. const oldChecklist = Blaze.getData(formerParent.get(0)).checklist;
  25. if (oldChecklist._id !== checklist._id) {
  26. const currentItem = Blaze.getData(ui.item.get(0)).item;
  27. for (let i = 0; i < orderedItems.length; i++) {
  28. let itemId = orderedItems[i];
  29. if (itemId !== currentItem._id) continue;
  30. checklist.addItem(currentItem.title);
  31. checklist = Checklists.findOne({_id: checklist._id});
  32. itemId = checklist._id + (checklist.newItemIndex - 1);
  33. if (currentItem.finished) {
  34. checklist.finishItem(itemId);
  35. }
  36. orderedItems[i] = itemId;
  37. oldChecklist.removeItem(currentItem._id);
  38. }
  39. }
  40. checklist.sortItems(orderedItems);
  41. },
  42. });
  43. }
  44. Template.checklists.onRendered(function () {
  45. const self = BlazeComponent.getComponentForElement(this.firstNode);
  46. self.itemsDom = this.$('.card-checklist-items');
  47. initSorting(self.itemsDom);
  48. self.itemsDom.mousedown(function(evt) {
  49. evt.stopPropagation();
  50. });
  51. function userIsMember() {
  52. return Meteor.user() && Meteor.user().isBoardMember();
  53. }
  54. // Disable sorting if the current user is not a board member
  55. self.autorun(() => {
  56. const $itemsDom = $(self.itemsDom);
  57. if ($itemsDom.data('sortable')) {
  58. $(self.itemsDom).sortable('option', 'disabled', !userIsMember());
  59. }
  60. });
  61. });
  62. BlazeComponent.extendComponent({
  63. addChecklist(event) {
  64. event.preventDefault();
  65. const textarea = this.find('textarea.js-add-checklist-item');
  66. const title = textarea.value.trim();
  67. const cardId = this.currentData().cardId;
  68. const card = Cards.findOne(cardId);
  69. if (title) {
  70. Checklists.insert({
  71. cardId,
  72. title,
  73. sort: card.checklists().count(),
  74. });
  75. setTimeout(() => {
  76. this.$('.add-checklist-item').last().click();
  77. }, 100);
  78. }
  79. textarea.value = '';
  80. textarea.focus();
  81. },
  82. addChecklistItem(event) {
  83. event.preventDefault();
  84. const textarea = this.find('textarea.js-add-checklist-item');
  85. const title = textarea.value.trim();
  86. const checklist = this.currentData().checklist;
  87. if (title) {
  88. checklist.addItem(title);
  89. }
  90. // We keep the form opened, empty it.
  91. textarea.value = '';
  92. textarea.focus();
  93. },
  94. editChecklist(event) {
  95. event.preventDefault();
  96. const textarea = this.find('textarea.js-edit-checklist-item');
  97. const title = textarea.value.trim();
  98. const checklist = this.currentData().checklist;
  99. checklist.setTitle(title);
  100. },
  101. canModifyCard() {
  102. return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
  103. },
  104. editChecklistItem(event) {
  105. event.preventDefault();
  106. const textarea = this.find('textarea.js-edit-checklist-item');
  107. const title = textarea.value.trim();
  108. const itemId = this.currentData().item._id;
  109. const checklist = this.currentData().checklist;
  110. checklist.editItem(itemId, title);
  111. },
  112. deleteItem() {
  113. const checklist = this.currentData().checklist;
  114. const item = this.currentData().item;
  115. if (checklist && item && item._id) {
  116. checklist.removeItem(item._id);
  117. }
  118. },
  119. deleteChecklist() {
  120. const checklist = this.currentData().checklist;
  121. if (checklist && checklist._id) {
  122. Checklists.remove(checklist._id);
  123. }
  124. },
  125. pressKey(event) {
  126. //If user press enter key inside a form, submit it, so user doesn't have to leave keyboard to submit a form.
  127. if (event.keyCode === 13) {
  128. event.preventDefault();
  129. const $form = $(event.currentTarget).closest('form');
  130. $form.find('button[type=submit]').click();
  131. }
  132. },
  133. events() {
  134. return [{
  135. 'submit .js-add-checklist': this.addChecklist,
  136. 'submit .js-edit-checklist-title': this.editChecklist,
  137. 'submit .js-add-checklist-item': this.addChecklistItem,
  138. 'submit .js-edit-checklist-item': this.editChecklistItem,
  139. 'click .js-delete-checklist-item': this.deleteItem,
  140. 'click .js-delete-checklist': this.deleteChecklist,
  141. keydown: this.pressKey,
  142. }];
  143. },
  144. }).register('checklists');
  145. Template.itemDetail.helpers({
  146. canModifyCard() {
  147. return Meteor.user() && Meteor.user().isBoardMember() && !Meteor.user().isCommentOnly();
  148. },
  149. });
  150. BlazeComponent.extendComponent({
  151. toggleItem() {
  152. const checklist = this.currentData().checklist;
  153. const item = this.currentData().item;
  154. if (checklist && item && item._id) {
  155. checklist.toggleItem(item._id);
  156. }
  157. },
  158. events() {
  159. return [{
  160. 'click .item .check-box': this.toggleItem,
  161. }];
  162. },
  163. }).register('itemDetail');