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>