aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src/views/Navigation.vue
diff options
context:
space:
mode:
authorChristopher Ng <chrng8@gmail.com>2024-07-30 18:19:55 -0700
committerChristopher Ng <chrng8@gmail.com>2024-08-01 09:17:56 -0700
commit3fabc4f7336bbf93d65acc918e1300616091dd54 (patch)
tree6d9969307c21dde46c9e0e621e73827a16c9e524 /apps/files/src/views/Navigation.vue
parent855a45650e608ff797b5ae38a7f18fa24a474f3f (diff)
downloadnextcloud-server-3fabc4f7336bbf93d65acc918e1300616091dd54.tar.gz
nextcloud-server-3fabc4f7336bbf93d65acc918e1300616091dd54.zip
feat: Navigate via folder tree
Signed-off-by: Christopher Ng <chrng8@gmail.com>
Diffstat (limited to 'apps/files/src/views/Navigation.vue')
-rw-r--r--apps/files/src/views/Navigation.vue127
1 files changed, 32 insertions, 95 deletions
diff --git a/apps/files/src/views/Navigation.vue b/apps/files/src/views/Navigation.vue
index b0588863f5d..cca38824d2c 100644
--- a/apps/files/src/views/Navigation.vue
+++ b/apps/files/src/views/Navigation.vue
@@ -4,38 +4,14 @@
-->
<template>
<NcAppNavigation data-cy-files-navigation
+ class="files-navigation"
:aria-label="t('files', 'Files')">
<template #search>
<NcAppNavigationSearch v-model="searchQuery" :label="t('files', 'Filter filenames…')" />
</template>
<template #default>
<NcAppNavigationList :aria-label="t('files', 'Views')">
- <NcAppNavigationItem v-for="view in parentViews"
- :key="view.id"
- :allow-collapse="true"
- :data-cy-files-navigation-item="view.id"
- :exact="useExactRouteMatching(view)"
- :icon="view.iconClass"
- :name="view.name"
- :open="isExpanded(view)"
- :pinned="view.sticky"
- :to="generateToNavigation(view)"
- @update:open="onToggleExpand(view)">
- <!-- Sanitized icon as svg if provided -->
- <NcIconSvgWrapper v-if="view.icon" slot="icon" :svg="view.icon" />
-
- <!-- Child views if any -->
- <NcAppNavigationItem v-for="child in childViews[view.id]"
- :key="child.id"
- :data-cy-files-navigation-item="child.id"
- :exact-path="true"
- :icon="child.iconClass"
- :name="child.name"
- :to="generateToNavigation(child)">
- <!-- Sanitized icon as svg if provided -->
- <NcIconSvgWrapper v-if="child.icon" slot="icon" :svg="child.icon" />
- </NcAppNavigationItem>
- </NcAppNavigationItem>
+ <FilesNavigationItem :views="viewMap" />
</NcAppNavigationList>
<!-- Settings modal-->
@@ -65,7 +41,7 @@
import type { View } from '@nextcloud/files'
import { emit } from '@nextcloud/event-bus'
-import { t } from '@nextcloud/l10n'
+import { translate as t, getCanonicalLocale, getLanguage } from '@nextcloud/l10n'
import { defineComponent } from 'vue'
import IconCog from 'vue-material-design-icons/Cog.vue'
@@ -73,9 +49,9 @@ import NcAppNavigation from '@nextcloud/vue/dist/Components/NcAppNavigation.js'
import NcAppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
import NcAppNavigationList from '@nextcloud/vue/dist/Components/NcAppNavigationList.js'
import NcAppNavigationSearch from '@nextcloud/vue/dist/Components/NcAppNavigationSearch.js'
-import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js'
import NavigationQuota from '../components/NavigationQuota.vue'
import SettingsModal from './Settings.vue'
+import FilesNavigationItem from '../components/FilesNavigationItem.vue'
import { useNavigation } from '../composables/useNavigation'
import { useFilenameFilter } from '../composables/useFilenameFilter'
@@ -83,18 +59,26 @@ import { useFiltersStore } from '../store/filters.ts'
import { useViewConfigStore } from '../store/viewConfig.ts'
import logger from '../logger.ts'
+const collator = Intl.Collator(
+ [getLanguage(), getCanonicalLocale()],
+ {
+ numeric: true,
+ usage: 'sort',
+ },
+)
+
export default defineComponent({
name: 'Navigation',
components: {
IconCog,
+ FilesNavigationItem,
NavigationQuota,
NcAppNavigation,
NcAppNavigationItem,
NcAppNavigationList,
NcAppNavigationSearch,
- NcIconSvgWrapper,
SettingsModal,
},
@@ -129,28 +113,21 @@ export default defineComponent({
return this.$route?.params?.view || 'files'
},
- parentViews(): View[] {
- return this.views
- // filter child views
- .filter(view => !view.parent)
- // sort views by order
- .sort((a, b) => {
- return a.order - b.order
- })
- },
-
- childViews(): Record<string, View[]> {
+ /**
+ * Map of parent ids to views
+ */
+ viewMap(): Record<string, View[]> {
return this.views
- // filter parent views
- .filter(view => !!view.parent)
- // create a map of parents and their children
- .reduce((list, view) => {
- list[view.parent!] = [...(list[view.parent!] || []), view]
- // Sort children by order
- list[view.parent!].sort((a, b) => {
- return a.order - b.order
+ .reduce((map, view) => {
+ map[view.parent!] = [...(map[view.parent!] || []), view]
+ // TODO Allow undefined order for natural sort
+ map[view.parent!].sort((a, b) => {
+ if (typeof a.order === 'number' || typeof b.order === 'number') {
+ return (a.order ?? 0) - (b.order ?? 0)
+ }
+ return collator.compare(a.name, b.name)
})
- return list
+ return map
}, {} as Record<string, View[]>)
},
},
@@ -176,16 +153,6 @@ export default defineComponent({
methods: {
/**
- * Only use exact route matching on routes with child views
- * Because if a view does not have children (like the files view) then multiple routes might be matched for it
- * Like for the 'files' view this does not work because of optional 'fileid' param so /files and /files/1234 are both in the 'files' view
- * @param view The view to check
- */
- useExactRouteMatching(view: View): boolean {
- return this.childViews[view.id]?.length > 0
- },
-
- /**
* Set the view as active on the navigation and handle internal state
* @param view View to set active
*/
@@ -197,42 +164,6 @@ export default defineComponent({
},
/**
- * Expand/collapse a a view with children and permanently
- * save this setting in the server.
- * @param view View to toggle
- */
- onToggleExpand(view: View) {
- // Invert state
- const isExpanded = this.isExpanded(view)
- // Update the view expanded state, might not be necessary
- view.expanded = !isExpanded
- this.viewConfigStore.update(view.id, 'expanded', !isExpanded)
- },
-
- /**
- * Check if a view is expanded by user config
- * or fallback to the default value.
- * @param view View to check if expanded
- */
- isExpanded(view: View): boolean {
- return typeof this.viewConfigStore.getConfig(view.id)?.expanded === 'boolean'
- ? this.viewConfigStore.getConfig(view.id).expanded === true
- : view.expanded === true
- },
-
- /**
- * Generate the route to a view
- * @param view View to generate "to" navigation for
- */
- generateToNavigation(view: View) {
- if (view.params) {
- const { dir } = view.params
- return { name: 'filelist', params: view.params, query: { dir } }
- }
- return { name: 'filelist', params: { view: view.id } }
- },
-
- /**
* Open the settings modal
*/
openSettings() {
@@ -272,4 +203,10 @@ export default defineComponent({
// Prevent shrinking or growing
flex: 0 0 auto;
}
+
+.files-navigation {
+ :deep(.app-navigation__content > ul.app-navigation__list) {
+ will-change: scroll-position;
+ }
+}
</style>