Show changed files as file-tree (#5379)

This commit is contained in:
Anbraten 2025-07-31 23:49:42 +02:00 committed by GitHub
parent f6b3e16e4c
commit c659128f31
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 110 additions and 4 deletions

View file

@ -0,0 +1,70 @@
<template>
<div>
<div
class="group hover:bg-wp-background-200 dark:hover:bg-wp-background-100 flex cursor-pointer items-center rounded-md px-2 py-1.5 transition-all duration-150"
:class="{ 'font-medium': node.isDirectory }"
tabindex="0"
role="button"
:aria-expanded="node.isDirectory ? !collapsed : undefined"
:aria-label="node.isDirectory ? `${collapsed ? 'Expand' : 'Collapse'} folder ${node.name}` : `File ${node.name}`"
@click="collapsed = !collapsed"
@keydown.enter="collapsed = !collapsed"
@keydown.space="collapsed = !collapsed"
>
<div class="mr-1 flex w-4 items-center justify-start">
<Icon
v-if="node.isDirectory"
name="chevron-right"
class="text-wp-text-alt-100 group-hover:text-wp-text-200 h-6 min-w-6 transition-transform duration-150"
:class="{ 'rotate-90 transform': !collapsed }"
/>
</div>
<Icon
:name="iconName"
class="text-wp-text-alt-100 group-hover:text-wp-text-200 mr-3 transition-colors duration-150"
/>
<span
class="text-wp-text-200 group-hover:text-wp-text-100 truncate text-sm transition-colors duration-150"
:class="{ 'text-wp-text-100': node.isDirectory }"
:title="node.name"
>
{{ node.name }}
</span>
</div>
<div
v-if="node.isDirectory && !collapsed"
class="border-wp-background-300 mt-1 ml-2 border-l pl-1 transition-all duration-200"
>
<FileTree v-for="child in node.children" :key="child.path" :node="child" />
</div>
</div>
</template>
<script lang="ts" setup name="FileTreeNode">
import { computed, ref } from 'vue';
import Icon from '~/components/atomic/Icon.vue';
export interface TreeNode {
name: string;
path: string;
isDirectory: boolean;
children: TreeNode[];
}
const props = defineProps<{
node: TreeNode;
}>();
const collapsed = ref(false);
const iconName = computed(() => {
if (props.node.isDirectory) {
return collapsed.value ? 'folder' : 'folder-open';
}
return 'file';
});
</script>

View file

@ -61,6 +61,9 @@
<SvgIcon v-else-if="name === 'tray-full'" :path="mdiTrayFull" size="1.3rem" />
<SvgIcon v-else-if="name === 'file-cog-outline'" :path="mdiFileCogOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'file-edit-outline'" :path="mdiFileEditOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'folder'" :path="mdiFolderOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'folder-open'" :path="mdiFolderOpenOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'file'" :path="mdiFileOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'bug-outline'" :path="mdiBugOutline" size="1.3rem" />
<SvgIcon v-else-if="name === 'docker'" :path="mdiDocker" size="1.3rem" />
<SvgIcon v-else-if="name === 'forge'" :path="mdiCodeBraces" size="1.3rem" />
@ -124,6 +127,9 @@ import {
mdiEyeOutline,
mdiFileCogOutline,
mdiFileEditOutline,
mdiFileOutline,
mdiFolderOpenOutline,
mdiFolderOutline,
mdiFormatListBulleted,
mdiFormatListGroup,
mdiGestureTap,
@ -230,7 +236,10 @@ export type IconNames =
| 'org'
| 'cron'
| 'toolbox'
| 'forge';
| 'forge'
| 'folder'
| 'folder-open'
| 'file';
const props = defineProps<{
name: IconNames;

View file

@ -1,8 +1,8 @@
<template>
<Panel>
<ul class="w-full list-inside list-disc">
<li v-for="file in pipeline.changed_files" :key="file">{{ file }}</li>
</ul>
<div class="w-full">
<FileTree v-for="node in fileTree" :key="node.name" :node="node" :depth="0" />
</div>
</Panel>
</template>
@ -10,6 +10,8 @@
import { computed } from 'vue';
import { useI18n } from 'vue-i18n';
import FileTree from '~/components/FileTree.vue';
import type { TreeNode } from '~/components/FileTree.vue';
import Panel from '~/components/layout/Panel.vue';
import { requiredInject } from '~/compositions/useInjectProvide';
import { useWPTitle } from '~/compositions/useWPTitle';
@ -25,4 +27,29 @@ useWPTitle(
repo.value.full_name,
]),
);
const fileTree = computed(() =>
(pipeline.value.changed_files ?? []).reduce((acc, file) => {
const parts = file.split('/');
let currentLevel = acc;
parts.forEach((part, index) => {
const existingNode = currentLevel.find((node) => node.name === part);
if (existingNode) {
currentLevel = existingNode.children;
} else {
const newNode = {
name: part,
path: parts.slice(0, index + 1).join('/'),
isDirectory: index < parts.length - 1,
children: [],
};
currentLevel.push(newNode);
currentLevel = newNode.children;
}
});
return acc;
}, [] as TreeNode[]),
);
</script>