|
1 | 1 | <script lang="ts" setup> |
2 | 2 | import {SvgIcon, type SvgName} from '../svg.ts'; |
3 | | -import {diffTreeStore} from '../modules/stores.ts'; |
4 | | -import {computed, nextTick, ref, watch} from 'vue'; |
5 | | -import type {Item, DirItem, File, FileStatus} from '../utils/filetree.ts'; |
| 3 | +import {ref} from 'vue'; |
| 4 | +import {type DiffStatus, type DiffTreeEntry, diffTreeStore} from '../modules/diff-file.ts'; |
6 | 5 |
|
7 | | -// ------------------------------------------------------------ |
8 | | -// Component Props |
9 | | -// ------------------------------------------------------------ |
10 | | -const props = defineProps<{ |
11 | | - item: Item, |
12 | | - setViewed?:(val: boolean) => void, |
| 6 | +defineProps<{ |
| 7 | + item: DiffTreeEntry, |
13 | 8 | }>(); |
14 | 9 |
|
15 | | -// ------------------------------------------------------------ |
16 | | -// Reactive State & Refs |
17 | | -// ------------------------------------------------------------ |
18 | 10 | const store = diffTreeStore(); |
19 | | -const count = ref(0); |
20 | | -let pendingUpdate = 0; |
21 | | -let pendingTimer: Promise<void> | undefined; |
22 | | -// ------------------------------------------------------------ |
23 | | -// Batch Update Mechanism |
24 | | -// ------------------------------------------------------------ |
25 | | -/** |
26 | | - * Handles batched updates to count value |
27 | | - * Prevents multiple updates within the same tick |
28 | | - */ |
29 | | -const setCount = (isViewed: boolean) => { |
30 | | - pendingUpdate += (isViewed ? 1 : -1); |
| 11 | +const collapsed = ref(false); |
31 | 12 |
|
32 | | - if (pendingTimer === undefined) { |
33 | | - pendingTimer = nextTick(() => { |
34 | | - count.value = Math.max(0, count.value + pendingUpdate); |
35 | | - pendingUpdate = 0; |
36 | | - pendingTimer = undefined; |
37 | | - }); |
38 | | - } |
39 | | -}; |
40 | | -
|
41 | | -// ------------------------------------------------------------ |
42 | | -// Computed Properties |
43 | | -// ------------------------------------------------------------ |
44 | | -/** |
45 | | - * Determines viewed state based on item type: |
46 | | - * - Files: Directly use IsViewed property |
47 | | - * - Directories: Compare children count with viewed count |
48 | | - */ |
49 | | -const isViewed = computed(() => { |
50 | | - return props.item.isFile ? props.item.file.IsViewed : (props.item as DirItem).children.length === count.value; |
51 | | -}); |
52 | | -// ------------------------------------------------------------ |
53 | | -// Watchers & Side Effects |
54 | | -// ------------------------------------------------------------ |
55 | | -/** |
56 | | - * Propagate viewed state changes to parent component |
57 | | - * Using post flush to ensure DOM updates are complete |
58 | | - */ |
59 | | -watch( |
60 | | - () => isViewed.value, |
61 | | - (newVal) => { |
62 | | - if (props.setViewed) { |
63 | | - props.setViewed(newVal); |
64 | | - } |
65 | | - }, |
66 | | - {immediate: true, flush: 'post'}, |
67 | | -); |
68 | | -
|
69 | | -// ------------------------------------------------------------ |
70 | | -// Collapse Behavior Documentation |
71 | | -// ------------------------------------------------------------ |
72 | | -/** |
73 | | - * Collapse behavior rules: |
74 | | - * - Viewed folders start collapsed initially |
75 | | - * - Manual expand/collapse takes precedence over automatic behavior |
76 | | - * - State persists after manual interaction |
77 | | - */ |
78 | | -const collapsed = ref(isViewed.value); |
79 | | -
|
80 | | -function getIconForDiffStatus(pType: FileStatus) { |
81 | | - const diffTypes: Record<FileStatus, { name: SvgName, classes: Array<string> }> = { |
| 13 | +function getIconForDiffStatus(pType: DiffStatus) { |
| 14 | + const diffTypes: Record<DiffStatus, { name: SvgName, classes: Array<string> }> = { |
82 | 15 | 'added': {name: 'octicon-diff-added', classes: ['text', 'green']}, |
83 | 16 | 'modified': {name: 'octicon-diff-modified', classes: ['text', 'yellow']}, |
84 | 17 | 'deleted': {name: 'octicon-diff-removed', classes: ['text', 'red']}, |
85 | 18 | 'renamed': {name: 'octicon-diff-renamed', classes: ['text', 'teal']}, |
86 | 19 | 'copied': {name: 'octicon-diff-renamed', classes: ['text', 'green']}, |
87 | 20 | 'typechange': {name: 'octicon-diff-modified', classes: ['text', 'green']}, // there is no octicon for copied, so renamed should be ok |
88 | 21 | }; |
89 | | - return diffTypes[pType]; |
| 22 | + return diffTypes[pType] ?? {name: 'octicon-blocked', classes: ['text', 'red']}; |
90 | 23 | } |
91 | 24 |
|
92 | | -function fileIcon(file: File) { |
93 | | - if (file.IsSubmodule) { |
| 25 | +function entryIcon(entry: DiffTreeEntry) { |
| 26 | + if (entry.EntryMode === 'commit') { |
94 | 27 | return 'octicon-file-submodule'; |
95 | 28 | } |
96 | 29 | return 'octicon-file'; |
97 | 30 | } |
98 | 31 | </script> |
99 | 32 |
|
100 | 33 | <template> |
101 | | - <!--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"--> |
102 | | - <a |
103 | | - v-if="item.isFile" class="item-file" |
104 | | - :class="{ 'selected': store.selectedItem === '#diff-' + item.file.NameHash, 'viewed': isViewed }" |
105 | | - :title="item.name" :href="'#diff-' + item.file.NameHash" |
106 | | - > |
107 | | - <!-- file --> |
108 | | - <SvgIcon :name="fileIcon(item.file)"/> |
109 | | - <span class="gt-ellipsis tw-flex-1">{{ item.name }}</span> |
110 | | - <SvgIcon |
111 | | - :name="getIconForDiffStatus(item.file.Status).name" |
112 | | - :class="getIconForDiffStatus(item.file.Status).classes" |
113 | | - /> |
114 | | - </a> |
115 | | - |
116 | | - <template v-else-if="item.isFile === false"> |
117 | | - <div class="item-directory" :class="{ 'viewed': isViewed }" :title="item.name" @click.stop="collapsed = !collapsed"> |
| 34 | + <template v-if="item.EntryMode === 'tree'"> |
| 35 | + <div class="item-directory" :class="{ 'viewed': item.IsViewed }" :title="item.DisplayName" @click.stop="collapsed = !collapsed"> |
118 | 36 | <!-- directory --> |
119 | 37 | <SvgIcon :name="collapsed ? 'octicon-chevron-right' : 'octicon-chevron-down'"/> |
120 | 38 | <SvgIcon |
121 | 39 | class="text primary" |
122 | 40 | :name="collapsed ? 'octicon-file-directory-fill' : 'octicon-file-directory-open-fill'" |
123 | 41 | /> |
124 | | - <span class="gt-ellipsis">{{ item.name }}</span> |
| 42 | + <span class="gt-ellipsis">{{ item.DisplayName }}</span> |
125 | 43 | </div> |
126 | 44 |
|
127 | 45 | <div v-show="!collapsed" class="sub-items"> |
128 | | - <DiffFileTreeItem v-for="childItem in item.children" :key="childItem.name" :item="childItem" :set-viewed="setCount"/> |
| 46 | + <DiffFileTreeItem v-for="childItem in item.Children" :key="childItem.DisplayName" :item="childItem"/> |
129 | 47 | </div> |
130 | 48 | </template> |
| 49 | + <a |
| 50 | + v-else |
| 51 | + class="item-file" :class="{ 'selected': store.selectedItem === '#diff-' + item.NameHash, 'viewed': item.IsViewed }" |
| 52 | + :title="item.DisplayName" :href="'#diff-' + item.NameHash" |
| 53 | + > |
| 54 | + <!-- file --> |
| 55 | + <SvgIcon :name="entryIcon(item)"/> |
| 56 | + <span class="gt-ellipsis tw-flex-1">{{ item.DisplayName }}</span> |
| 57 | + <SvgIcon |
| 58 | + :name="getIconForDiffStatus(item.DiffStatus).name" |
| 59 | + :class="getIconForDiffStatus(item.DiffStatus).classes" |
| 60 | + /> |
| 61 | + </a> |
| 62 | + |
131 | 63 | </template> |
132 | 64 | <style scoped> |
133 | 65 | a, |
|
0 commit comments