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.

DiffFileTree.vue 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. <script>
  2. import DiffFileTreeItem from './DiffFileTreeItem.vue';
  3. import {loadMoreFiles} from '../features/repo-diff.js';
  4. import {toggleElem} from '../utils/dom.js';
  5. import {diffTreeStore} from '../modules/stores.js';
  6. import {setFileFolding} from '../features/file-fold.js';
  7. const LOCAL_STORAGE_KEY = 'diff_file_tree_visible';
  8. export default {
  9. components: {DiffFileTreeItem},
  10. data: () => {
  11. return {store: diffTreeStore()};
  12. },
  13. computed: {
  14. fileTree() {
  15. const result = [];
  16. for (const file of this.store.files) {
  17. // Split file into directories
  18. const splits = file.Name.split('/');
  19. let index = 0;
  20. let parent = null;
  21. let isFile = false;
  22. for (const split of splits) {
  23. index += 1;
  24. // reached the end
  25. if (index === splits.length) {
  26. isFile = true;
  27. }
  28. let newParent = {
  29. name: split,
  30. children: [],
  31. isFile,
  32. };
  33. if (isFile === true) {
  34. newParent.file = file;
  35. }
  36. if (parent) {
  37. // check if the folder already exists
  38. const existingFolder = parent.children.find(
  39. (x) => x.name === split,
  40. );
  41. if (existingFolder) {
  42. newParent = existingFolder;
  43. } else {
  44. parent.children.push(newParent);
  45. }
  46. } else {
  47. const existingFolder = result.find((x) => x.name === split);
  48. if (existingFolder) {
  49. newParent = existingFolder;
  50. } else {
  51. result.push(newParent);
  52. }
  53. }
  54. parent = newParent;
  55. }
  56. }
  57. const mergeChildIfOnlyOneDir = (entries) => {
  58. for (const entry of entries) {
  59. if (entry.children) {
  60. mergeChildIfOnlyOneDir(entry.children);
  61. }
  62. if (entry.children.length === 1 && entry.children[0].isFile === false) {
  63. // Merge it to the parent
  64. entry.name = `${entry.name}/${entry.children[0].name}`;
  65. entry.children = entry.children[0].children;
  66. }
  67. }
  68. };
  69. // Merge folders with just a folder as children in order to
  70. // reduce the depth of our tree.
  71. mergeChildIfOnlyOneDir(result);
  72. return result;
  73. },
  74. },
  75. mounted() {
  76. // Default to true if unset
  77. this.store.fileTreeIsVisible = localStorage.getItem(LOCAL_STORAGE_KEY) !== 'false';
  78. document.querySelector('.diff-toggle-file-tree-button').addEventListener('click', this.toggleVisibility);
  79. this.hashChangeListener = () => {
  80. this.store.selectedItem = window.location.hash;
  81. this.expandSelectedFile();
  82. };
  83. this.hashChangeListener();
  84. window.addEventListener('hashchange', this.hashChangeListener);
  85. },
  86. unmounted() {
  87. document.querySelector('.diff-toggle-file-tree-button').removeEventListener('click', this.toggleVisibility);
  88. window.removeEventListener('hashchange', this.hashChangeListener);
  89. },
  90. methods: {
  91. expandSelectedFile() {
  92. // expand file if the selected file is folded
  93. if (this.store.selectedItem) {
  94. const box = document.querySelector(this.store.selectedItem);
  95. const folded = box?.getAttribute('data-folded') === 'true';
  96. if (folded) setFileFolding(box, box.querySelector('.fold-file'), false);
  97. }
  98. },
  99. toggleVisibility() {
  100. this.updateVisibility(!this.store.fileTreeIsVisible);
  101. },
  102. updateVisibility(visible) {
  103. this.store.fileTreeIsVisible = visible;
  104. localStorage.setItem(LOCAL_STORAGE_KEY, this.store.fileTreeIsVisible);
  105. this.updateState(this.store.fileTreeIsVisible);
  106. },
  107. updateState(visible) {
  108. const btn = document.querySelector('.diff-toggle-file-tree-button');
  109. const [toShow, toHide] = btn.querySelectorAll('.icon');
  110. const tree = document.getElementById('diff-file-tree');
  111. const newTooltip = btn.getAttribute(visible ? 'data-hide-text' : 'data-show-text');
  112. btn.setAttribute('data-tooltip-content', newTooltip);
  113. toggleElem(tree, visible);
  114. toggleElem(toShow, !visible);
  115. toggleElem(toHide, visible);
  116. },
  117. loadMoreData() {
  118. loadMoreFiles(this.store.linkLoadMore);
  119. },
  120. },
  121. };
  122. </script>
  123. <template>
  124. <div v-if="store.fileTreeIsVisible" class="diff-file-tree-items">
  125. <!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
  126. <DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item"/>
  127. <div v-if="store.isIncomplete" class="gt-pt-2">
  128. <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
  129. </div>
  130. </div>
  131. </template>
  132. <style scoped>
  133. .diff-file-tree-items {
  134. display: flex;
  135. flex-direction: column;
  136. gap: 1px;
  137. margin-right: .5rem;
  138. }
  139. </style>