subtasks.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. BlazeComponent.extendComponent({
  2. canModifyCard() {
  3. return (
  4. Meteor.user() &&
  5. Meteor.user().isBoardMember() &&
  6. !Meteor.user().isCommentOnly() &&
  7. !Meteor.user().isWorker()
  8. );
  9. },
  10. }).register('subtaskDetail');
  11. BlazeComponent.extendComponent({
  12. addSubtask(event) {
  13. event.preventDefault();
  14. const textarea = this.find('textarea.js-add-subtask-item');
  15. const title = textarea.value.trim();
  16. const cardId = this.currentData().cardId;
  17. const card = Cards.findOne(cardId);
  18. const sortIndex = -1;
  19. const crtBoard = Boards.findOne(card.boardId);
  20. const targetBoard = crtBoard.getDefaultSubtasksBoard();
  21. const listId = targetBoard.getDefaultSubtasksListId();
  22. const swimlaneId = targetBoard.getDefaultSwimline()._id;
  23. if (title) {
  24. const _id = Cards.insert({
  25. title,
  26. parentId: cardId,
  27. members: [],
  28. labelIds: [],
  29. customFields: [],
  30. listId,
  31. boardId: targetBoard._id,
  32. sort: sortIndex,
  33. swimlaneId,
  34. type: 'cardType-card',
  35. });
  36. // In case the filter is active we need to add the newly inserted card in
  37. // the list of exceptions -- cards that are not filtered. Otherwise the
  38. // card will disappear instantly.
  39. // See https://github.com/wekan/wekan/issues/80
  40. Filter.addException(_id);
  41. setTimeout(() => {
  42. this.$('.add-subtask-item')
  43. .last()
  44. .click();
  45. }, 100);
  46. }
  47. textarea.value = '';
  48. textarea.focus();
  49. },
  50. canModifyCard() {
  51. return (
  52. Meteor.user() &&
  53. Meteor.user().isBoardMember() &&
  54. !Meteor.user().isCommentOnly() &&
  55. !Meteor.user().isWorker()
  56. );
  57. },
  58. deleteSubtask() {
  59. const subtask = this.currentData().subtask;
  60. if (subtask && subtask._id) {
  61. subtask.archive();
  62. this.toggleDeleteDialog.set(false);
  63. }
  64. },
  65. editSubtask(event) {
  66. event.preventDefault();
  67. const textarea = this.find('textarea.js-edit-subtask-item');
  68. const title = textarea.value.trim();
  69. const subtask = this.currentData().subtask;
  70. subtask.setTitle(title);
  71. },
  72. onCreated() {
  73. this.toggleDeleteDialog = new ReactiveVar(false);
  74. this.subtaskToDelete = null; //Store data context to pass to subtaskDeleteDialog template
  75. },
  76. pressKey(event) {
  77. //If user press enter key inside a form, submit it
  78. //Unless the user is also holding down the 'shift' key
  79. if (event.keyCode === 13 && !event.shiftKey) {
  80. event.preventDefault();
  81. const $form = $(event.currentTarget).closest('form');
  82. $form.find('button[type=submit]').click();
  83. }
  84. },
  85. events() {
  86. const events = {
  87. 'click .toggle-delete-subtask-dialog'(event) {
  88. if ($(event.target).hasClass('js-delete-subtask')) {
  89. this.subtaskToDelete = this.currentData().subtask; //Store data context
  90. }
  91. this.toggleDeleteDialog.set(!this.toggleDeleteDialog.get());
  92. },
  93. 'click .js-view-subtask'(event) {
  94. if ($(event.target).hasClass('js-view-subtask')) {
  95. const subtask = this.currentData().subtask;
  96. const board = subtask.board();
  97. FlowRouter.go('card', {
  98. boardId: board._id,
  99. slug: board.slug,
  100. cardId: subtask._id,
  101. });
  102. }
  103. },
  104. };
  105. return [
  106. {
  107. ...events,
  108. 'submit .js-add-subtask': this.addSubtask,
  109. 'submit .js-edit-subtask-title': this.editSubtask,
  110. 'click .confirm-subtask-delete': this.deleteSubtask,
  111. keydown: this.pressKey,
  112. },
  113. ];
  114. },
  115. }).register('subtasks');
  116. Template.subtaskDeleteDialog.onCreated(() => {
  117. const $cardDetails = this.$('.card-details');
  118. this.scrollState = {
  119. position: $cardDetails.scrollTop(), //save current scroll position
  120. top: false, //required for smooth scroll animation
  121. };
  122. //Callback's purpose is to only prevent scrolling after animation is complete
  123. $cardDetails.animate({ scrollTop: 0 }, 500, () => {
  124. this.scrollState.top = true;
  125. });
  126. //Prevent scrolling while dialog is open
  127. $cardDetails.on('scroll', () => {
  128. if (this.scrollState.top) {
  129. //If it's already in position, keep it there. Otherwise let animation scroll
  130. $cardDetails.scrollTop(0);
  131. }
  132. });
  133. });
  134. Template.subtaskDeleteDialog.onDestroyed(() => {
  135. const $cardDetails = this.$('.card-details');
  136. $cardDetails.off('scroll'); //Reactivate scrolling
  137. $cardDetails.animate({ scrollTop: this.scrollState.position });
  138. });
  139. Template.subtaskItemDetail.helpers({
  140. canModifyCard() {
  141. return (
  142. Meteor.user() &&
  143. Meteor.user().isBoardMember() &&
  144. !Meteor.user().isCommentOnly() &&
  145. !Meteor.user().isWorker()
  146. );
  147. },
  148. });
  149. BlazeComponent.extendComponent({
  150. // ...
  151. }).register('subtaskItemDetail');