subtasks.js 4.4 KB

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