Files
gites/src/components/RefTree.vue
T
szaman e4e597dc5c
ci / check (push) Successful in 3m3s
release / release (push) Successful in 11m34s
Eye on the Tags header hides all tags, also tags added later
2026-10-15 13:55:08 +02:00

93 lines
3.2 KiB
Vue

<script setup lang="ts">
import { isRefVisible } from "../lib/filter";
import { refsUnder, type TreeNode } from "../lib/tree";
import type { FilterState, Ref, RefKind } from "../lib/types";
import EyeButton from "./EyeButton.vue";
import RefIcon from "./RefIcon.vue";
const props = defineProps<{
nodes: TreeNode[];
kind: RefKind;
depth: number;
filters: FilterState;
headRef: string | null;
/** Paths of folders the user closed. Shared by the whole tree. */
closed: Set<string>;
}>();
const emit = defineEmits<{ jump: [ref: Ref]; toggle: [names: string[]] }>();
const allHidden = (n: TreeNode) => refsUnder(n).every((r) => props.filters.hidden.includes(r.name));
const anyVisible = (n: TreeNode) => refsUnder(n).some((r) => isRefVisible(r.name, props.filters));
// Keyed by kind too, so a `backup` tag folder and a `backup` branch folder are separate.
const key = (n: TreeNode) => `${props.kind}:${n.path}`;
function toggleFolder(n: TreeNode) {
if (!props.closed.delete(key(n))) props.closed.add(key(n));
}
</script>
<template>
<template v-for="n in nodes" :key="n.path">
<div
class="item"
:class="{ off: !anyVisible(n), head: n.ref?.name === headRef, folder: n.children.length }"
:style="{ paddingLeft: 10 + depth * 14 + 'px' }"
@click="n.ref ? emit('jump', n.ref) : toggleFolder(n)"
>
<span
v-if="n.children.length"
class="arrow"
:class="{ open: !closed.has(key(n)) }"
@click.stop="toggleFolder(n)"
>▸</span>
<span v-else class="arrow" />
<RefIcon v-if="n.ref" :kind="kind" />
<svg v-else class="folder-icon" viewBox="0 0 16 16" width="12" height="12" fill="none" stroke="currentColor" stroke-width="1.5">
<path d="M1.5 4a1 1 0 0 1 1-1h3.5l1.5 1.5h6a1 1 0 0 1 1 1V12a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1z" />
</svg>
<span class="name" :title="n.ref?.name ?? n.path">{{ n.label }}</span>
<EyeButton
v-if="kind === 'tag' && filters.hideTags"
class="locked"
:shut="true"
disabled
title="all tags are hidden: use the eye next to Tags"
@click.stop
/>
<EyeButton v-else :shut="allHidden(n)" @click.stop="emit('toggle', refsUnder(n).map((r) => r.name))" />
</div>
<RefTree
v-if="n.children.length && !closed.has(key(n))"
:nodes="n.children"
:kind="kind"
:depth="depth + 1"
:filters="filters"
:head-ref="headRef"
:closed="closed"
@jump="emit('jump', $event)"
@toggle="emit('toggle', $event)"
/>
</template>
</template>
<style scoped>
.item {
display: flex;
align-items: center;
gap: 6px;
padding: 2px 8px 2px 10px;
cursor: pointer;
color: var(--text);
}
.item:hover { background: var(--bg-hover); }
.item.off { color: var(--text-dim); opacity: 0.55; }
.item.head .name { font-weight: 600; color: #fff; }
.item.folder { color: #c5cae0; }
.folder-icon { flex: none; color: #ffd43b; opacity: 0.8; }
.arrow { width: 10px; flex: none; color: var(--text-dim); font-size: 10px; transition: transform 0.12s; }
.arrow.open { transform: rotate(90deg); }
.name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.item:hover :deep(.eye) { opacity: 1; }
.item :deep(.eye.locked) { opacity: 0.35; cursor: default; }
</style>