diff options
author | Christopher Ng <chrng8@gmail.com> | 2024-07-30 18:19:55 -0700 |
---|---|---|
committer | Christopher Ng <chrng8@gmail.com> | 2024-08-01 09:17:56 -0700 |
commit | 3fabc4f7336bbf93d65acc918e1300616091dd54 (patch) | |
tree | 6d9969307c21dde46c9e0e621e73827a16c9e524 /apps/files/src/views/Navigation.vue | |
parent | 855a45650e608ff797b5ae38a7f18fa24a474f3f (diff) | |
download | nextcloud-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.vue | 127 |
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> |