subtasks.js 4.4 KB

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