@@ -8,6 +8,7 @@ import type { IView } from '@nextcloud/files'
88
99import { getCanonicalLocale , getLanguage } from ' @nextcloud/l10n'
1010import { computed , onMounted , ref } from ' vue'
11+ import { useRoute } from ' vue-router/composables'
1112import NcAppNavigationItem from ' @nextcloud/vue/components/NcAppNavigationItem'
1213import NcIconSvgWrapper from ' @nextcloud/vue/components/NcIconSvgWrapper'
1314import { useVisibleViews } from ' ../composables/useViews.ts'
@@ -21,12 +22,32 @@ const props = withDefaults(defineProps<{
2122 level: 0 ,
2223})
2324
25+ /**
26+ * Views whose main file list is driven by `dir`. The route often carries a `fileid` for the
27+ * selected row; many actions switch `view` between `folders`, `files`, and `personal` while
28+ * keeping the same directory — the folder tree should stay highlighted based on `dir` only.
29+ */
30+ const PATH_BASED_LISTING_VIEW_IDS = new Set <string >([folderTreeId , ' files' , ' personal' ])
31+
2432const maxLevel = 6 // Limit nesting to not exceed max call stack size
2533const viewConfigStore = useViewConfigStore ()
2634const viewConfig = computed (() => viewConfigStore .viewConfigs [props .view .id ])
35+ const route = useRoute ()
2736const isExpanded = computed (() => viewConfig .value
2837 ? (viewConfig .value .expanded === true )
2938 : (props .view .expanded === true ))
39+ const isFolderTreeNode = computed (() => props .view .id .startsWith (` ${folderTreeId }:: ` ))
40+ const isDirectoryActive = computed (() => {
41+ if (! isFolderTreeNode .value ) {
42+ return false
43+ }
44+
45+ const currentView = String (route .params ?.view ?? ' ' )
46+ const currentDir = normalizeDir (route .query ?.dir )
47+ const viewDir = normalizeDir (props .view .params ?.dir )
48+
49+ return PATH_BASED_LISTING_VIEW_IDS .has (currentView ) && currentDir === viewDir
50+ })
3051
3152const views = useVisibleViews ()
3253const childViews = computed (() => {
@@ -66,6 +87,16 @@ const hasChildViews = computed(() => childViews.value.length > 0)
6687const navigationRoute = computed (() => {
6788 if (props .view .params ) {
6889 const { dir } = props .view .params
90+ // Folder tree: navigate by `dir` only (no `fileid` in the path). Matches how the list
91+ // is located (`?dir=…`) even when a file row is selected (`/:fileid`).
92+ if (isFolderTreeNode .value ) {
93+ const dirParam = typeof dir === ' string' ? dir : ' /'
94+ return {
95+ name: ' filelist' ,
96+ params: { view: folderTreeId },
97+ query: { dir: dirParam },
98+ }
99+ }
69100 return { name: ' filelist' , params: { ... props .view .params }, query: { dir } }
70101 }
71102 return { name: ' filelist' , params: { view: props .view .id } }
@@ -121,6 +152,18 @@ async function loadChildViews() {
121152 }
122153 }
123154}
155+
156+ /**
157+ * Normalize directory paths for route comparisons.
158+ *
159+ * @param dir - the route directory to normalize
160+ */
161+ function normalizeDir(dir : unknown ): string {
162+ if (typeof dir !== ' string' || dir .length === 0 ) {
163+ return ' /'
164+ }
165+ return dir .replace (/ ^ (. + )\/ $ / , ' $1' )
166+ }
124167 </script >
125168
126169<script lang="ts">
@@ -141,7 +184,8 @@ export default {
141184 allow-collapse
142185 :loading =" isLoading"
143186 :data-cy-files-navigation-item =" view.id"
144- :exact =" hasChildViews /* eslint-disable-line @nextcloud/vue/no-deprecated-props */"
187+ :exact =" isFolderTreeNode || hasChildViews /* eslint-disable-line @nextcloud/vue/no-deprecated-props */"
188+ :active =" isDirectoryActive"
145189 :name =" view.name"
146190 :open =" isExpanded"
147191 :pinned =" view.sticky"
0 commit comments