swimlaneHeader.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. import { TAPi18n } from '/imports/i18n';
  2. import { ReactiveCache } from '/imports/reactiveCache';
  3. const { calculateIndexData } = Utils;
  4. let swimlaneColors;
  5. Meteor.startup(() => {
  6. swimlaneColors = Swimlanes.simpleSchema()._schema.color.allowedValues;
  7. });
  8. BlazeComponent.extendComponent({
  9. editTitle(event) {
  10. event.preventDefault();
  11. const newTitle = this.childComponents('inlinedForm')[0]
  12. .getValue()
  13. .trim();
  14. const swimlane = this.currentData();
  15. if (newTitle) {
  16. swimlane.rename(newTitle.trim());
  17. }
  18. },
  19. collapsed(check = undefined) {
  20. const swimlane = Template.currentData();
  21. const status = swimlane.isCollapsed();
  22. if (check === undefined) {
  23. // just check
  24. return status;
  25. } else {
  26. swimlane.collapse(!status);
  27. return !status;
  28. }
  29. },
  30. events() {
  31. return [
  32. {
  33. 'click .js-collapse-swimlane'(event) {
  34. event.preventDefault();
  35. this.collapsed(!this.collapsed());
  36. },
  37. 'click .js-open-swimlane-menu': Popup.open('swimlaneAction'),
  38. 'click .js-open-add-swimlane-menu': Popup.open('swimlaneAdd'),
  39. submit: this.editTitle,
  40. },
  41. ];
  42. },
  43. }).register('swimlaneHeader');
  44. Template.swimlaneFixedHeader.helpers({
  45. isBoardAdmin() {
  46. return ReactiveCache.getCurrentUser().isBoardAdmin();
  47. },
  48. isTitleDefault(title) {
  49. // https://github.com/wekan/wekan/issues/4763
  50. // https://github.com/wekan/wekan/issues/4742
  51. // Translation text for "default" does not work, it returns an object.
  52. // When that happens, try use translation "defaultdefault" that has same content of default, or return text "Default".
  53. // This can happen, if swimlane does not have name.
  54. // Yes, this is fixing the symptom (Swimlane title does not have title)
  55. // instead of fixing the problem (Add Swimlane title when creating swimlane)
  56. // because there could be thousands of swimlanes, adding name Default to all of them
  57. // would be very slow.
  58. if (title.startsWith("key 'default") && title.endsWith('returned an object instead of string.')) {
  59. if (`${TAPi18n.__('defaultdefault')}`.startsWith("key 'default") && `${TAPi18n.__('defaultdefault')}`.endsWith('returned an object instead of string.')) {
  60. return 'Default';
  61. } else {
  62. return `${TAPi18n.__('defaultdefault')}`;
  63. }
  64. } else if (title === 'Default') {
  65. return `${TAPi18n.__('defaultdefault')}`;
  66. } else {
  67. return title;
  68. }
  69. },
  70. });
  71. Template.editSwimlaneTitleForm.helpers({
  72. isTitleDefault(title) {
  73. // https://github.com/wekan/wekan/issues/4763
  74. // https://github.com/wekan/wekan/issues/4742
  75. // Translation text for "default" does not work, it returns an object.
  76. // When that happens, try use translation "defaultdefault" that has same content of default, or return text "Default".
  77. // This can happen, if swimlane does not have name.
  78. // Yes, this is fixing the symptom (Swimlane title does not have title)
  79. // instead of fixing the problem (Add Swimlane title when creating swimlane)
  80. // because there could be thousands of swimlanes, adding name Default to all of them
  81. // would be very slow.
  82. if (title.startsWith("key 'default") && title.endsWith('returned an object instead of string.')) {
  83. if (`${TAPi18n.__('defaultdefault')}`.startsWith("key 'default") && `${TAPi18n.__('defaultdefault')}`.endsWith('returned an object instead of string.')) {
  84. return 'Default';
  85. } else {
  86. return `${TAPi18n.__('defaultdefault')}`;
  87. }
  88. } else if (title === 'Default') {
  89. return `${TAPi18n.__('defaultdefault')}`;
  90. } else {
  91. return title;
  92. }
  93. },
  94. });
  95. Template.swimlaneActionPopup.events({
  96. 'click .js-set-swimlane-color': Popup.open('setSwimlaneColor'),
  97. 'click .js-set-swimlane-height': Popup.open('setSwimlaneHeight'),
  98. 'click .js-close-swimlane'(event) {
  99. event.preventDefault();
  100. this.archive();
  101. Popup.back();
  102. },
  103. 'click .js-move-swimlane': Popup.open('moveSwimlane'),
  104. 'click .js-copy-swimlane': Popup.open('copySwimlane'),
  105. });
  106. Template.swimlaneActionPopup.events({
  107. isCommentOnly() {
  108. return ReactiveCache.getCurrentUser().isCommentOnly();
  109. },
  110. });
  111. BlazeComponent.extendComponent({
  112. onCreated() {
  113. this.currentSwimlane = this.currentData();
  114. },
  115. events() {
  116. return [
  117. {
  118. submit(event) {
  119. event.preventDefault();
  120. const currentBoard = Utils.getCurrentBoard();
  121. const nextSwimlane = currentBoard.nextSwimlane(this.currentSwimlane);
  122. const titleInput = this.find('.swimlane-name-input');
  123. const title = titleInput.value.trim();
  124. const sortValue = calculateIndexData(
  125. this.currentSwimlane,
  126. nextSwimlane,
  127. 1,
  128. );
  129. const swimlaneType = currentBoard.isTemplatesBoard()
  130. ? 'template-swimlane'
  131. : 'swimlane';
  132. if (title) {
  133. Swimlanes.insert({
  134. title,
  135. boardId: Session.get('currentBoard'),
  136. sort: sortValue.base || 0,
  137. type: swimlaneType,
  138. });
  139. titleInput.value = '';
  140. titleInput.focus();
  141. }
  142. // XXX ideally, we should move the popup to the newly
  143. // created swimlane so a user can add more than one swimlane
  144. // with a minimum of interactions
  145. Popup.back();
  146. },
  147. 'click .js-swimlane-template': Popup.open('searchElement'),
  148. },
  149. ];
  150. },
  151. }).register('swimlaneAddPopup');
  152. BlazeComponent.extendComponent({
  153. onCreated() {
  154. this.currentSwimlane = this.currentData();
  155. this.currentColor = new ReactiveVar(this.currentSwimlane.color);
  156. },
  157. colors() {
  158. return swimlaneColors.map(color => ({ color, name: '' }));
  159. },
  160. isSelected(color) {
  161. return this.currentColor.get() === color;
  162. },
  163. events() {
  164. return [
  165. {
  166. 'click .js-palette-color'() {
  167. this.currentColor.set(this.currentData().color);
  168. },
  169. 'click .js-submit'() {
  170. this.currentSwimlane.setColor(this.currentColor.get());
  171. Popup.back();
  172. },
  173. 'click .js-remove-color'() {
  174. this.currentSwimlane.setColor(null);
  175. Popup.back();
  176. },
  177. },
  178. ];
  179. },
  180. }).register('setSwimlaneColorPopup');
  181. BlazeComponent.extendComponent({
  182. onCreated() {
  183. this.currentSwimlane = this.currentData();
  184. },
  185. applySwimlaneHeight() {
  186. const swimlane = this.currentData();
  187. const board = swimlane.boardId;
  188. const height = parseInt(
  189. Template.instance()
  190. .$('.swimlane-height-value')
  191. .val(),
  192. 10,
  193. );
  194. // FIXME(mark-i-m): where do we put constants?
  195. // also in imports/i18n/data/en.i18n.json
  196. if (height != -1 && (height < 100 || !height)) {
  197. Template.instance()
  198. .$('.swimlane-height-error')
  199. .click();
  200. } else {
  201. Meteor.call('applySwimlaneHeight', board, swimlane._id, height);
  202. Popup.back();
  203. }
  204. },
  205. swimlaneHeightValue() {
  206. const swimlane = this.currentData();
  207. const board = swimlane.boardId;
  208. return ReactiveCache.getCurrentUser().getSwimlaneHeight(board, swimlane._id);
  209. },
  210. events() {
  211. return [
  212. {
  213. 'click .swimlane-height-apply': this.applySwimlaneHeight,
  214. 'click .swimlane-height-error': Popup.open('swimlaneHeightError'),
  215. },
  216. ];
  217. },
  218. }).register('setSwimlaneHeightPopup');