aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files/src/components')
-rw-r--r--apps/files/src/components/FilesListTableHeaderActions.vue5
-rw-r--r--apps/files/src/components/FilesNavigationItem.vue8
-rw-r--r--apps/files/src/components/FilesNavigationSearch.vue122
-rw-r--r--apps/files/src/components/NavigationQuota.vue2
4 files changed, 134 insertions, 3 deletions
diff --git a/apps/files/src/components/FilesListTableHeaderActions.vue b/apps/files/src/components/FilesListTableHeaderActions.vue
index 4d2f2f361e6..53b7e7ef21b 100644
--- a/apps/files/src/components/FilesListTableHeaderActions.vue
+++ b/apps/files/src/components/FilesListTableHeaderActions.vue
@@ -6,6 +6,7 @@
<div class="files-list__column files-list__row-actions-batch" data-cy-files-list-selection-actions>
<NcActions ref="actionsMenu"
container="#app-content-vue"
+ :boundaries-element="boundariesElement"
:disabled="!!loading || areSomeNodesLoading"
:force-name="true"
:inline="enabledInlineActions.length"
@@ -123,6 +124,8 @@ export default defineComponent({
const fileListWidth = useFileListWidth()
const { directory } = useRouteParameters()
+ const boundariesElement = document.getElementById('app-content-vue')
+
return {
directory,
fileListWidth,
@@ -130,6 +133,8 @@ export default defineComponent({
actionsMenuStore,
filesStore,
selectionStore,
+
+ boundariesElement,
}
},
diff --git a/apps/files/src/components/FilesNavigationItem.vue b/apps/files/src/components/FilesNavigationItem.vue
index 372a83e1441..c29bc00c67f 100644
--- a/apps/files/src/components/FilesNavigationItem.vue
+++ b/apps/files/src/components/FilesNavigationItem.vue
@@ -89,7 +89,7 @@ export default defineComponent({
return (Object.values(this.views).reduce((acc, views) => [...acc, ...views], []) as View[])
.filter(view => view.params?.dir.startsWith(this.parent.params?.dir))
}
- return this.views[this.parent.id] ?? [] // Root level views have `undefined` parent ids
+ return this.filterVisible(this.views[this.parent.id] ?? [])
},
style() {
@@ -103,11 +103,15 @@ export default defineComponent({
},
methods: {
+ filterVisible(views: View[]) {
+ return views.filter(({ id, hidden }) => id === this.currentView?.id || hidden !== true)
+ },
+
hasChildViews(view: View): boolean {
if (this.level >= maxLevel) {
return false
}
- return this.views[view.id]?.length > 0
+ return this.filterVisible(this.views[view.id] ?? []).length > 0
},
/**
diff --git a/apps/files/src/components/FilesNavigationSearch.vue b/apps/files/src/components/FilesNavigationSearch.vue
new file mode 100644
index 00000000000..85dc5534e5e
--- /dev/null
+++ b/apps/files/src/components/FilesNavigationSearch.vue
@@ -0,0 +1,122 @@
+<!--
+ - SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ - SPDX-License-Identifier: AGPL-3.0-or-later
+-->
+
+<script setup lang="ts">
+import { mdiMagnify, mdiSearchWeb } from '@mdi/js'
+import { t } from '@nextcloud/l10n'
+import { computed } from 'vue'
+import NcActions from '@nextcloud/vue/components/NcActions'
+import NcActionButton from '@nextcloud/vue/components/NcActionButton'
+import NcAppNavigationSearch from '@nextcloud/vue/components/NcAppNavigationSearch'
+import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
+import { onBeforeNavigation } from '../composables/useBeforeNavigation.ts'
+import { useNavigation } from '../composables/useNavigation.ts'
+import { useRouteParameters } from '../composables/useRouteParameters.ts'
+import { useFilesStore } from '../store/files.ts'
+import { useSearchStore } from '../store/search.ts'
+import { VIEW_ID } from '../views/search.ts'
+
+const { currentView } = useNavigation(true)
+const { directory } = useRouteParameters()
+
+const filesStore = useFilesStore()
+const searchStore = useSearchStore()
+
+/**
+ * When the route is changed from search view to something different
+ * we need to clear the search box.
+ */
+onBeforeNavigation((to, from, next) => {
+ if (to.params.view !== VIEW_ID && from.params.view === VIEW_ID) {
+ // we are leaving the search view so unset the query
+ searchStore.query = ''
+ searchStore.scope = 'filter'
+ } else if (to.params.view === VIEW_ID && from.params.view === VIEW_ID) {
+ // fix the query if the user refreshed the view
+ if (searchStore.query && !to.query.query) {
+ // @ts-expect-error This is a weird issue with vue-router v4 and will be fixed in v5 (vue 3)
+ return next({
+ ...to,
+ query: {
+ ...to.query,
+ query: searchStore.query,
+ },
+ })
+ }
+ }
+ next()
+})
+
+/**
+ * Are we currently on the search view.
+ * Needed to disable the action menu (we cannot change the search mode there)
+ */
+const isSearchView = computed(() => currentView.value.id === VIEW_ID)
+
+/**
+ * Local search is only possible on real DAV resources within the files root
+ */
+const canSearchLocally = computed(() => {
+ if (searchStore.base) {
+ return true
+ }
+
+ const folder = filesStore.getDirectoryByPath(currentView.value.id, directory.value)
+ return folder?.isDavResource && folder?.root?.startsWith('/files/')
+})
+
+/**
+ * Different searchbox label depending if filtering or searching
+ */
+const searchLabel = computed(() => {
+ if (searchStore.scope === 'globally') {
+ return t('files', 'Search globally by filename …')
+ } else if (searchStore.scope === 'locally') {
+ return t('files', 'Search here by filename …')
+ }
+ return t('files', 'Filter file names …')
+})
+
+/**
+ * Update the search value and set the base if needed
+ * @param value - The new value
+ */
+function onUpdateSearch(value: string) {
+ if (searchStore.scope === 'locally' && currentView.value.id !== VIEW_ID) {
+ searchStore.base = filesStore.getDirectoryByPath(currentView.value.id, directory.value)
+ }
+ searchStore.query = value
+}
+</script>
+
+<template>
+ <NcAppNavigationSearch :label="searchLabel" :model-value="searchStore.query" @update:modelValue="onUpdateSearch">
+ <template #actions>
+ <NcActions :aria-label="t('files', 'Search scope options')" :disabled="isSearchView">
+ <template #icon>
+ <NcIconSvgWrapper :path="searchStore.scope === 'globally' ? mdiSearchWeb : mdiMagnify" />
+ </template>
+ <NcActionButton close-after-click @click="searchStore.scope = 'filter'">
+ <template #icon>
+ <NcIconSvgWrapper :path="mdiMagnify" />
+ </template>
+ {{ t('files', 'Filter in current view') }}
+ </NcActionButton>
+ <NcActionButton v-if="canSearchLocally" close-after-click @click="searchStore.scope = 'locally'">
+ <template #icon>
+ <NcIconSvgWrapper :path="mdiMagnify" />
+ </template>
+ {{ t('files', 'Search from this location') }}
+ </NcActionButton>
+ <NcActionButton close-after-click @click="searchStore.scope = 'globally'">
+ <template #icon>
+ <NcIconSvgWrapper :path="mdiSearchWeb" />
+ </template>
+ {{ t('files', 'Search globally') }}
+ </NcActionButton>
+ </NcActions>
+ </template>
+ </NcAppNavigationSearch>
+</template>
diff --git a/apps/files/src/components/NavigationQuota.vue b/apps/files/src/components/NavigationQuota.vue
index f1d2738e81d..fd10af1c495 100644
--- a/apps/files/src/components/NavigationQuota.vue
+++ b/apps/files/src/components/NavigationQuota.vue
@@ -58,7 +58,7 @@ export default {
computed: {
storageStatsTitle() {
const usedQuotaByte = formatFileSize(this.storageStats?.used, false, false)
- const quotaByte = formatFileSize(this.storageStats?.quota, false, false)
+ const quotaByte = formatFileSize(this.storageStats?.total, false, false)
// If no quota set
if (this.storageStats?.quota < 0) {