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