Allow repository admins to hide specific folders from the code browser for non-admin users. Hidden folders are shown dimmed to admins but completely hidden from regular users. Includes database migration, settings UI, tree filtering logic, and frontend support for toggling visibility.
130 lines
3.5 KiB
Vue
130 lines
3.5 KiB
Vue
<script lang="ts" setup>
|
|
import {SvgIcon} from '../svg.ts';
|
|
import {isPlainClick} from '../utils/dom.ts';
|
|
import {shouldTriggerAreYouSure} from '../vendor/jquery.are-you-sure.ts';
|
|
import {shallowRef} from 'vue';
|
|
import type {createViewFileTreeStore, FileTreeItem} from './ViewFileTreeStore.ts';
|
|
|
|
const props = defineProps<{
|
|
item: FileTreeItem,
|
|
store: ReturnType<typeof createViewFileTreeStore>
|
|
}>();
|
|
|
|
const store = props.store;
|
|
const isLoading = shallowRef(false);
|
|
const children = shallowRef(props.item.children);
|
|
const collapsed = shallowRef(!props.item.children);
|
|
|
|
const doLoadChildren = async () => {
|
|
collapsed.value = !collapsed.value;
|
|
if (!collapsed.value) {
|
|
isLoading.value = true;
|
|
try {
|
|
children.value = await store.loadChildren(props.item.fullPath);
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
};
|
|
|
|
const onItemClick = (e: MouseEvent) => {
|
|
// only handle the click event with partial page reloading if both
|
|
// - the user didn't press any special key like "Ctrl+Click" (which may have custom browser behavior)
|
|
// - the editor/commit form isn't dirty (a full page reload shows a confirmation dialog if the form contains unsaved changes)
|
|
if (!isPlainClick(e) || shouldTriggerAreYouSure()) return;
|
|
e.preventDefault();
|
|
if (props.item.entryMode === 'tree') doLoadChildren();
|
|
store.navigateTreeView(props.item.fullPath);
|
|
};
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<a
|
|
class="tree-item silenced"
|
|
:class="{
|
|
'selected': store.selectedItem === item.fullPath,
|
|
'type-submodule': item.entryMode === 'commit',
|
|
'type-directory': item.entryMode === 'tree',
|
|
'type-symlink': item.entryMode === 'symlink',
|
|
'type-file': item.entryMode === 'blob' || item.entryMode === 'exec',
|
|
'is-hidden-folder': item.isHidden,
|
|
}"
|
|
:title="item.entryName"
|
|
:href="store.buildTreePathWebUrl(item.fullPath)"
|
|
@click.stop="onItemClick"
|
|
>
|
|
<div v-if="item.entryMode === 'tree'" class="item-toggle">
|
|
<SvgIcon v-if="isLoading" name="gitea-running" class="rotate-clockwise"/>
|
|
<SvgIcon v-else :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'" @click.stop.prevent="doLoadChildren"/>
|
|
</div>
|
|
<div class="item-content">
|
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
|
<span class="tw-contents" v-html="(!collapsed && item.entryIconOpen) ? item.entryIconOpen : item.entryIcon"/>
|
|
<span class="gt-ellipsis">{{ item.entryName }}</span>
|
|
</div>
|
|
</a>
|
|
|
|
<div v-if="children?.length" v-show="!collapsed" class="sub-items">
|
|
<ViewFileTreeItem v-for="childItem in children" :key="childItem.entryName" :item="childItem" :store="store"/>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.sub-items {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1px;
|
|
margin-left: 14px;
|
|
border-left: 1px solid var(--color-secondary);
|
|
}
|
|
|
|
.tree-item.selected {
|
|
color: var(--color-text);
|
|
background: var(--color-active);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.tree-item.type-directory {
|
|
user-select: none;
|
|
}
|
|
|
|
.tree-item {
|
|
display: grid;
|
|
grid-template-columns: 16px 1fr;
|
|
grid-template-areas: "toggle content";
|
|
gap: 0.25em;
|
|
padding: 6px;
|
|
}
|
|
|
|
.tree-item:hover {
|
|
color: var(--color-text);
|
|
background: var(--color-hover);
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.item-toggle {
|
|
grid-area: toggle;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.item-content {
|
|
grid-area: content;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5em;
|
|
text-overflow: ellipsis;
|
|
min-width: 0;
|
|
}
|
|
|
|
.tree-item.is-hidden-folder {
|
|
opacity: 0.45;
|
|
}
|
|
|
|
.tree-item.is-hidden-folder:hover {
|
|
opacity: 0.7;
|
|
}
|
|
</style>
|