You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

repo-projects.js 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. const {csrfToken} = window.config;
  2. async function initRepoProjectSortable() {
  3. const els = document.querySelectorAll('#project-board > .board');
  4. if (!els.length) return;
  5. const {Sortable} = await import(/* webpackChunkName: "sortable" */'sortablejs');
  6. // the HTML layout is: #project-board > .board > .board-column .board.cards > .board-card.card .content
  7. const mainBoard = els[0];
  8. let boardColumns = mainBoard.getElementsByClassName('board-column');
  9. new Sortable(mainBoard, {
  10. group: 'board-column',
  11. draggable: '.board-column',
  12. filter: '[data-id="0"]',
  13. animation: 150,
  14. ghostClass: 'card-ghost',
  15. onSort: () => {
  16. boardColumns = mainBoard.getElementsByClassName('board-column');
  17. for (let i = 0; i < boardColumns.length; i++) {
  18. const column = boardColumns[i];
  19. if (parseInt($(column).data('sorting')) !== i) {
  20. $.ajax({
  21. url: $(column).data('url'),
  22. data: JSON.stringify({sorting: i, color: rgbToHex($(column).css('backgroundColor'))}),
  23. headers: {
  24. 'X-Csrf-Token': csrfToken,
  25. 'X-Remote': true,
  26. },
  27. contentType: 'application/json',
  28. method: 'PUT',
  29. });
  30. }
  31. }
  32. },
  33. });
  34. for (const boardColumn of boardColumns) {
  35. const boardCardList = boardColumn.getElementsByClassName('board')[0];
  36. new Sortable(boardCardList, {
  37. group: 'shared',
  38. animation: 150,
  39. ghostClass: 'card-ghost',
  40. onAdd: ({item, from, to, oldIndex}) => {
  41. const url = to.getAttribute('data-url');
  42. const issue = item.getAttribute('data-issue');
  43. $.ajax(`${url}/${issue}`, {
  44. headers: {
  45. 'X-Csrf-Token': csrfToken,
  46. 'X-Remote': true,
  47. },
  48. contentType: 'application/json',
  49. type: 'POST',
  50. error: () => {
  51. from.insertBefore(item, from.children[oldIndex]);
  52. },
  53. });
  54. },
  55. });
  56. }
  57. }
  58. export default function initRepoProject() {
  59. if (!$('.repository.projects').length) {
  60. return;
  61. }
  62. const _promise = initRepoProjectSortable();
  63. $('.edit-project-board').each(function () {
  64. const projectHeader = $(this).closest('.board-column-header');
  65. const projectTitleLabel = projectHeader.find('.board-label');
  66. const projectTitleInput = $(this).find(
  67. '.content > .form > .field > .project-board-title',
  68. );
  69. const projectColorInput = $(this).find('.content > .form > .field #new_board_color');
  70. const boardColumn = $(this).closest('.board-column');
  71. if (boardColumn.css('backgroundColor')) {
  72. setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor')));
  73. }
  74. $(this)
  75. .find('.content > .form > .actions > .red')
  76. .on('click', function (e) {
  77. e.preventDefault();
  78. $.ajax({
  79. url: $(this).data('url'),
  80. data: JSON.stringify({title: projectTitleInput.val(), color: projectColorInput.val()}),
  81. headers: {
  82. 'X-Csrf-Token': csrfToken,
  83. 'X-Remote': true,
  84. },
  85. contentType: 'application/json',
  86. method: 'PUT',
  87. }).done(() => {
  88. projectTitleLabel.text(projectTitleInput.val());
  89. projectTitleInput.closest('form').removeClass('dirty');
  90. if (projectColorInput.val()) {
  91. setLabelColor(projectHeader, projectColorInput.val());
  92. }
  93. boardColumn.attr('style', `background: ${projectColorInput.val()}!important`);
  94. $('.ui.modal').modal('hide');
  95. });
  96. });
  97. });
  98. $(document).on('click', '.set-default-project-board', async function (e) {
  99. e.preventDefault();
  100. await $.ajax({
  101. method: 'POST',
  102. url: $(this).data('url'),
  103. headers: {
  104. 'X-Csrf-Token': csrfToken,
  105. 'X-Remote': true,
  106. },
  107. contentType: 'application/json',
  108. });
  109. window.location.reload();
  110. });
  111. $('.delete-project-board').each(function () {
  112. $(this).click(function (e) {
  113. e.preventDefault();
  114. $.ajax({
  115. url: $(this).data('url'),
  116. headers: {
  117. 'X-Csrf-Token': csrfToken,
  118. 'X-Remote': true,
  119. },
  120. contentType: 'application/json',
  121. method: 'DELETE',
  122. }).done(() => {
  123. window.location.reload();
  124. });
  125. });
  126. });
  127. $('#new_board_submit').click(function (e) {
  128. e.preventDefault();
  129. const boardTitle = $('#new_board');
  130. const projectColorInput = $('#new_board_color_picker');
  131. $.ajax({
  132. url: $(this).data('url'),
  133. data: JSON.stringify({title: boardTitle.val(), color: projectColorInput.val()}),
  134. headers: {
  135. 'X-Csrf-Token': csrfToken,
  136. 'X-Remote': true,
  137. },
  138. contentType: 'application/json',
  139. method: 'POST',
  140. }).done(() => {
  141. boardTitle.closest('form').removeClass('dirty');
  142. window.location.reload();
  143. });
  144. });
  145. }
  146. function setLabelColor(label, color) {
  147. const red = getRelativeColor(parseInt(color.substr(1, 2), 16));
  148. const green = getRelativeColor(parseInt(color.substr(3, 2), 16));
  149. const blue = getRelativeColor(parseInt(color.substr(5, 2), 16));
  150. const luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
  151. if (luminance > 0.179) {
  152. label.removeClass('light-label').addClass('dark-label');
  153. } else {
  154. label.removeClass('dark-label').addClass('light-label');
  155. }
  156. }
  157. /**
  158. * Inspired by W3C recommandation https://www.w3.org/TR/WCAG20/#relativeluminancedef
  159. */
  160. function getRelativeColor(color) {
  161. color /= 255;
  162. return color <= 0.03928 ? color / 12.92 : ((color + 0.055) / 1.055) ** 2.4;
  163. }
  164. function rgbToHex(rgb) {
  165. rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
  166. return `#${hex(rgb[1])}${hex(rgb[2])}${hex(rgb[3])}`;
  167. }
  168. function hex(x) {
  169. const hexDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
  170. return Number.isNaN(x) ? '00' : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
  171. }