summaryrefslogtreecommitdiffstats
path: root/web_src/js/components/DiffFileTree.vue
diff options
context:
space:
mode:
authorDaniel Baumann <daniel@debian.org>2024-10-18 20:33:49 +0200
committerDaniel Baumann <daniel@debian.org>2024-10-18 20:33:49 +0200
commitdd136858f1ea40ad3c94191d647487fa4f31926c (patch)
tree58fec94a7b2a12510c9664b21793f1ed560c6518 /web_src/js/components/DiffFileTree.vue
parentInitial commit. (diff)
downloadforgejo-dd136858f1ea40ad3c94191d647487fa4f31926c.tar.xz
forgejo-dd136858f1ea40ad3c94191d647487fa4f31926c.zip
Adding upstream version 9.0.0.HEADupstream/9.0.0upstreamdebian
Signed-off-by: Daniel Baumann <daniel@debian.org>
Diffstat (limited to 'web_src/js/components/DiffFileTree.vue')
-rw-r--r--web_src/js/components/DiffFileTree.vue144
1 files changed, 144 insertions, 0 deletions
diff --git a/web_src/js/components/DiffFileTree.vue b/web_src/js/components/DiffFileTree.vue
new file mode 100644
index 0000000..cddfee1
--- /dev/null
+++ b/web_src/js/components/DiffFileTree.vue
@@ -0,0 +1,144 @@
+<script>
+import DiffFileTreeItem from './DiffFileTreeItem.vue';
+import {loadMoreFiles} from '../features/repo-diff.js';
+import {toggleElem} from '../utils/dom.js';
+import {diffTreeStore} from '../modules/stores.js';
+import {setFileFolding} from '../features/file-fold.js';
+
+const LOCAL_STORAGE_KEY = 'diff_file_tree_visible';
+
+export default {
+ components: {DiffFileTreeItem},
+ data: () => {
+ return {store: diffTreeStore()};
+ },
+ computed: {
+ fileTree() {
+ const result = [];
+ for (const file of this.store.files) {
+ // Split file into directories
+ const splits = file.Name.split('/');
+ let index = 0;
+ let parent = null;
+ let isFile = false;
+ for (const split of splits) {
+ index += 1;
+ // reached the end
+ if (index === splits.length) {
+ isFile = true;
+ }
+ let newParent = {
+ name: split,
+ children: [],
+ isFile,
+ };
+
+ if (isFile === true) {
+ newParent.file = file;
+ }
+
+ if (parent) {
+ // check if the folder already exists
+ const existingFolder = parent.children.find(
+ (x) => x.name === split,
+ );
+ if (existingFolder) {
+ newParent = existingFolder;
+ } else {
+ parent.children.push(newParent);
+ }
+ } else {
+ const existingFolder = result.find((x) => x.name === split);
+ if (existingFolder) {
+ newParent = existingFolder;
+ } else {
+ result.push(newParent);
+ }
+ }
+ parent = newParent;
+ }
+ }
+ const mergeChildIfOnlyOneDir = (entries) => {
+ for (const entry of entries) {
+ if (entry.children) {
+ mergeChildIfOnlyOneDir(entry.children);
+ }
+ if (entry.children.length === 1 && entry.children[0].isFile === false) {
+ // Merge it to the parent
+ entry.name = `${entry.name}/${entry.children[0].name}`;
+ entry.children = entry.children[0].children;
+ }
+ }
+ };
+ // Merge folders with just a folder as children in order to
+ // reduce the depth of our tree.
+ mergeChildIfOnlyOneDir(result);
+ return result;
+ },
+ },
+ mounted() {
+ // Default to true if unset
+ this.store.fileTreeIsVisible = localStorage.getItem(LOCAL_STORAGE_KEY) !== 'false';
+ document.querySelector('.diff-toggle-file-tree-button').addEventListener('click', this.toggleVisibility);
+
+ this.hashChangeListener = () => {
+ this.store.selectedItem = window.location.hash;
+ this.expandSelectedFile();
+ };
+ this.hashChangeListener();
+ window.addEventListener('hashchange', this.hashChangeListener);
+ },
+ unmounted() {
+ document.querySelector('.diff-toggle-file-tree-button').removeEventListener('click', this.toggleVisibility);
+ window.removeEventListener('hashchange', this.hashChangeListener);
+ },
+ methods: {
+ expandSelectedFile() {
+ // expand file if the selected file is folded
+ if (this.store.selectedItem) {
+ const box = document.querySelector(this.store.selectedItem);
+ const folded = box?.getAttribute('data-folded') === 'true';
+ if (folded) setFileFolding(box, box.querySelector('.fold-file'), false);
+ }
+ },
+ toggleVisibility() {
+ this.updateVisibility(!this.store.fileTreeIsVisible);
+ },
+ updateVisibility(visible) {
+ this.store.fileTreeIsVisible = visible;
+ localStorage.setItem(LOCAL_STORAGE_KEY, this.store.fileTreeIsVisible);
+ this.updateState(this.store.fileTreeIsVisible);
+ },
+ updateState(visible) {
+ const btn = document.querySelector('.diff-toggle-file-tree-button');
+ const [toShow, toHide] = btn.querySelectorAll('.icon');
+ const tree = document.getElementById('diff-file-tree');
+ const newTooltip = btn.getAttribute(visible ? 'data-hide-text' : 'data-show-text');
+ btn.setAttribute('data-tooltip-content', newTooltip);
+ toggleElem(tree, visible);
+ toggleElem(toShow, !visible);
+ toggleElem(toHide, visible);
+ },
+ loadMoreData() {
+ loadMoreFiles(this.store.linkLoadMore);
+ },
+ },
+};
+</script>
+<template>
+ <div v-if="store.fileTreeIsVisible" class="diff-file-tree-items">
+ <!-- only render the tree if we're visible. in many cases this is something that doesn't change very often -->
+ <DiffFileTreeItem v-for="item in fileTree" :key="item.name" :item="item"/>
+ <div v-if="store.isIncomplete" class="tw-pt-1">
+ <a :class="['ui', 'basic', 'tiny', 'button', store.isLoadingNewData ? 'disabled' : '']" @click.stop="loadMoreData">{{ store.showMoreMessage }}</a>
+ </div>
+ </div>
+</template>
+<style scoped>
+.diff-file-tree-items {
+ display: flex;
+ flex-direction: column;
+ gap: 1px;
+ margin-right: .5rem;
+}
+</style>