gitea源码

repo-projects.ts 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import {contrastColor} from '../utils/color.ts';
  2. import {createSortable} from '../modules/sortable.ts';
  3. import {POST, request} from '../modules/fetch.ts';
  4. import {fomanticQuery} from '../modules/fomantic/base.ts';
  5. import {queryElemChildren, queryElems, toggleElem} from '../utils/dom.ts';
  6. import type {SortableEvent} from 'sortablejs';
  7. import {toggleFullScreen} from '../utils.ts';
  8. function updateIssueCount(card: HTMLElement): void {
  9. const parent = card.parentElement;
  10. const count = parent.querySelectorAll('.issue-card').length;
  11. parent.querySelector('.project-column-issue-count').textContent = String(count);
  12. }
  13. async function moveIssue({item, from, to, oldIndex}: SortableEvent): Promise<void> {
  14. const columnCards = to.querySelectorAll('.issue-card');
  15. updateIssueCount(from);
  16. updateIssueCount(to);
  17. const columnSorting = {
  18. issues: Array.from(columnCards, (card, i) => ({
  19. issueID: parseInt(card.getAttribute('data-issue')),
  20. sorting: i,
  21. })),
  22. };
  23. try {
  24. await POST(`${to.getAttribute('data-url')}/move`, {
  25. data: columnSorting,
  26. });
  27. } catch (error) {
  28. console.error(error);
  29. from.insertBefore(item, from.children[oldIndex]);
  30. }
  31. }
  32. async function initRepoProjectSortable(): Promise<void> {
  33. // the HTML layout is: #project-board.board > .project-column .cards > .issue-card
  34. const mainBoard = document.querySelector('#project-board');
  35. let boardColumns = mainBoard.querySelectorAll<HTMLElement>('.project-column');
  36. createSortable(mainBoard, {
  37. group: 'project-column',
  38. draggable: '.project-column',
  39. handle: '.project-column-header',
  40. delayOnTouchOnly: true,
  41. delay: 500,
  42. onSort: async () => { // eslint-disable-line @typescript-eslint/no-misused-promises
  43. boardColumns = mainBoard.querySelectorAll<HTMLElement>('.project-column');
  44. const columnSorting = {
  45. columns: Array.from(boardColumns, (column, i) => ({
  46. columnID: parseInt(column.getAttribute('data-id')),
  47. sorting: i,
  48. })),
  49. };
  50. try {
  51. await POST(mainBoard.getAttribute('data-url'), {
  52. data: columnSorting,
  53. });
  54. } catch (error) {
  55. console.error(error);
  56. }
  57. },
  58. });
  59. for (const boardColumn of boardColumns) {
  60. const boardCardList = boardColumn.querySelector('.cards');
  61. createSortable(boardCardList, {
  62. group: 'shared',
  63. onAdd: moveIssue, // eslint-disable-line @typescript-eslint/no-misused-promises
  64. onUpdate: moveIssue, // eslint-disable-line @typescript-eslint/no-misused-promises
  65. delayOnTouchOnly: true,
  66. delay: 500,
  67. });
  68. }
  69. }
  70. function initRepoProjectColumnEdit(writableProjectBoard: Element): void {
  71. const elModal = document.querySelector<HTMLElement>('.ui.modal#project-column-modal-edit');
  72. const elForm = elModal.querySelector<HTMLFormElement>('form');
  73. const elColumnId = elForm.querySelector<HTMLInputElement>('input[name="id"]');
  74. const elColumnTitle = elForm.querySelector<HTMLInputElement>('input[name="title"]');
  75. const elColumnColor = elForm.querySelector<HTMLInputElement>('input[name="color"]');
  76. const attrDataColumnId = 'data-modal-project-column-id';
  77. const attrDataColumnTitle = 'data-modal-project-column-title-input';
  78. const attrDataColumnColor = 'data-modal-project-column-color-input';
  79. // the "new" button is not in project board, so need to query from document
  80. queryElems(document, '.show-project-column-modal-edit', (el) => {
  81. el.addEventListener('click', () => {
  82. elColumnId.value = el.getAttribute(attrDataColumnId);
  83. elColumnTitle.value = el.getAttribute(attrDataColumnTitle);
  84. elColumnColor.value = el.getAttribute(attrDataColumnColor);
  85. elColumnColor.dispatchEvent(new Event('input', {bubbles: true})); // trigger the color picker
  86. });
  87. });
  88. elForm.addEventListener('submit', async (e) => {
  89. e.preventDefault();
  90. const columnId = elColumnId.value;
  91. const actionBaseLink = elForm.getAttribute('data-action-base-link');
  92. const formData = new FormData(elForm);
  93. const formLink = columnId ? `${actionBaseLink}/${columnId}` : `${actionBaseLink}/columns/new`;
  94. const formMethod = columnId ? 'PUT' : 'POST';
  95. try {
  96. elForm.classList.add('is-loading');
  97. await request(formLink, {method: formMethod, data: formData});
  98. if (!columnId) {
  99. window.location.reload(); // newly added column, need to reload the page
  100. return;
  101. }
  102. // update the newly saved column title and color in the project board (to avoid reload)
  103. const elEditButton = writableProjectBoard.querySelector<HTMLButtonElement>(`.show-project-column-modal-edit[${attrDataColumnId}="${columnId}"]`);
  104. elEditButton.setAttribute(attrDataColumnTitle, elColumnTitle.value);
  105. elEditButton.setAttribute(attrDataColumnColor, elColumnColor.value);
  106. const elBoardColumn = writableProjectBoard.querySelector<HTMLElement>(`.project-column[data-id="${columnId}"]`);
  107. const elBoardColumnTitle = elBoardColumn.querySelector<HTMLElement>(`.project-column-title-text`);
  108. elBoardColumnTitle.textContent = elColumnTitle.value;
  109. if (elColumnColor.value) {
  110. const textColor = contrastColor(elColumnColor.value);
  111. elBoardColumn.style.setProperty('background', elColumnColor.value, 'important');
  112. elBoardColumn.style.setProperty('color', textColor, 'important');
  113. queryElemChildren<HTMLElement>(elBoardColumn, '.divider', (divider) => divider.style.color = textColor);
  114. } else {
  115. elBoardColumn.style.removeProperty('background');
  116. elBoardColumn.style.removeProperty('color');
  117. queryElemChildren<HTMLElement>(elBoardColumn, '.divider', (divider) => divider.style.removeProperty('color'));
  118. }
  119. fomanticQuery(elModal).modal('hide');
  120. } finally {
  121. elForm.classList.remove('is-loading');
  122. }
  123. });
  124. }
  125. function initRepoProjectToggleFullScreen(): void {
  126. const enterFullscreenBtn = document.querySelector('.screen-full');
  127. const exitFullscreenBtn = document.querySelector('.screen-normal');
  128. if (!enterFullscreenBtn || !exitFullscreenBtn) return;
  129. const toggleFullscreenState = (isFullScreen: boolean) => {
  130. toggleFullScreen('.projects-view', isFullScreen);
  131. toggleElem(enterFullscreenBtn, !isFullScreen);
  132. toggleElem(exitFullscreenBtn, isFullScreen);
  133. };
  134. enterFullscreenBtn.addEventListener('click', () => toggleFullscreenState(true));
  135. exitFullscreenBtn.addEventListener('click', () => toggleFullscreenState(false));
  136. }
  137. export function initRepoProject(): void {
  138. initRepoProjectToggleFullScreen();
  139. const writableProjectBoard = document.querySelector('#project-board[data-project-borad-writable="true"]');
  140. if (!writableProjectBoard) return;
  141. initRepoProjectSortable(); // no await
  142. initRepoProjectColumnEdit(writableProjectBoard);
  143. }