2
0

subtasks.js 4.6 KB

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