mirror of
https://github.com/go-gitea/gitea.git
synced 2025-04-25 19:08:48 +03:00

In the file tree, the icons are not vertically centered, which affects the overall visual consistency. Currently, the icons of submodules and symlinks do not adopt the value of entryIcon, resulting in inconsistent icon display. before:  after:  --------- Co-authored-by: silverwind <me@silverwind.io>
163 lines
4.3 KiB
Vue
163 lines
4.3 KiB
Vue
<script lang="ts" setup>
|
|
import {SvgIcon} from '../svg.ts';
|
|
import {ref} from 'vue';
|
|
|
|
type Item = {
|
|
entryName: string;
|
|
entryMode: string;
|
|
entryIcon: string;
|
|
entryIconOpen: string;
|
|
fullPath: string;
|
|
submoduleUrl?: string;
|
|
children?: Item[];
|
|
};
|
|
|
|
const props = defineProps<{
|
|
item: Item,
|
|
navigateViewContent:(treePath: string) => void,
|
|
loadChildren:(treePath: string, subPath?: string) => Promise<Item[]>,
|
|
selectedItem?: string,
|
|
}>();
|
|
|
|
const isLoading = ref(false);
|
|
const children = ref(props.item.children);
|
|
const collapsed = ref(!props.item.children);
|
|
|
|
const doLoadChildren = async () => {
|
|
collapsed.value = !collapsed.value;
|
|
if (!collapsed.value && props.loadChildren) {
|
|
isLoading.value = true;
|
|
try {
|
|
children.value = await props.loadChildren(props.item.fullPath);
|
|
} finally {
|
|
isLoading.value = false;
|
|
}
|
|
}
|
|
};
|
|
|
|
const doLoadDirContent = () => {
|
|
doLoadChildren();
|
|
props.navigateViewContent(props.item.fullPath);
|
|
};
|
|
|
|
const doLoadFileContent = () => {
|
|
props.navigateViewContent(props.item.fullPath);
|
|
};
|
|
|
|
const doGotoSubModule = () => {
|
|
location.href = props.item.submoduleUrl;
|
|
};
|
|
</script>
|
|
|
|
<!--title instead of tooltip above as the tooltip needs too much work with the current methods, i.e. not being loaded or staying open for "too long"-->
|
|
<template>
|
|
<div
|
|
v-if="item.entryMode === 'commit'" class="tree-item type-submodule"
|
|
:title="item.entryName"
|
|
@click.stop="doGotoSubModule"
|
|
>
|
|
<!-- submodule -->
|
|
<div class="item-content">
|
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
|
<span class="tw-contents" v-html="item.entryIcon"/>
|
|
<span class="gt-ellipsis tw-flex-1">{{ item.entryName }}</span>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-else-if="item.entryMode === 'symlink'" class="tree-item type-symlink"
|
|
:class="{'selected': selectedItem === item.fullPath}"
|
|
:title="item.entryName"
|
|
@click.stop="doLoadFileContent"
|
|
>
|
|
<!-- symlink -->
|
|
<div class="item-content">
|
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
|
<span class="tw-contents" v-html="item.entryIcon"/>
|
|
<span class="gt-ellipsis tw-flex-1">{{ item.entryName }}</span>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-else-if="item.entryMode !== 'tree'" class="tree-item type-file"
|
|
:class="{'selected': selectedItem === item.fullPath}"
|
|
:title="item.entryName"
|
|
@click.stop="doLoadFileContent"
|
|
>
|
|
<!-- file -->
|
|
<div class="item-content">
|
|
<!-- eslint-disable-next-line vue/no-v-html -->
|
|
<span class="tw-contents" v-html="item.entryIcon"/>
|
|
<span class="gt-ellipsis tw-flex-1">{{ item.entryName }}</span>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-else class="tree-item type-directory"
|
|
:class="{'selected': selectedItem === item.fullPath}"
|
|
:title="item.entryName"
|
|
@click.stop="doLoadDirContent"
|
|
>
|
|
<!-- directory -->
|
|
<div class="item-toggle">
|
|
<SvgIcon v-if="isLoading" name="octicon-sync" class="circular-spin"/>
|
|
<SvgIcon v-else :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'" @click.stop="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>
|
|
</div>
|
|
|
|
<div v-if="children?.length" v-show="!collapsed" class="sub-items">
|
|
<ViewFileTreeItem v-for="childItem in children" :key="childItem.entryName" :item="childItem" :selected-item="selectedItem" :navigate-view-content="navigateViewContent" :load-children="loadChildren"/>
|
|
</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;
|
|
}
|
|
</style>
|