aboutsummaryrefslogtreecommitdiffstats
path: root/core/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/components')
-rw-r--r--core/src/components/AccountMenu/AccountMenuEntry.vue38
-rw-r--r--core/src/components/AppMenuIcon.vue18
-rw-r--r--core/src/components/PublicPageMenu/PublicPageMenuEntry.vue8
-rw-r--r--core/src/components/UnifiedSearch/UnifiedSearchLocalSearchBar.vue4
-rw-r--r--core/src/components/UnifiedSearch/UnifiedSearchModal.vue92
-rw-r--r--core/src/components/login/PasswordLessLoginForm.vue2
-rw-r--r--core/src/components/setup/RecommendedApps.vue11
7 files changed, 95 insertions, 78 deletions
diff --git a/core/src/components/AccountMenu/AccountMenuEntry.vue b/core/src/components/AccountMenu/AccountMenuEntry.vue
index 47db84a7d33..d983226d273 100644
--- a/core/src/components/AccountMenu/AccountMenuEntry.vue
+++ b/core/src/components/AccountMenu/AccountMenuEntry.vue
@@ -11,28 +11,30 @@
compact
:href="href"
:name="name"
- target="_self">
+ target="_self"
+ @click="onClick">
<template #icon>
- <img class="account-menu-entry__icon"
+ <NcLoadingIcon v-if="loading" :size="20" class="account-menu-entry__loading" />
+ <slot v-else-if="$scopedSlots.icon" name="icon" />
+ <img v-else
+ class="account-menu-entry__icon"
:class="{ 'account-menu-entry__icon--active': active }"
:src="iconSource"
alt="">
</template>
- <template v-if="loading" #indicator>
- <NcLoadingIcon />
- </template>
</NcListItem>
</template>
-<script>
+<script lang="ts">
import { loadState } from '@nextcloud/initial-state'
+import { defineComponent } from 'vue'
import NcListItem from '@nextcloud/vue/components/NcListItem'
import NcLoadingIcon from '@nextcloud/vue/components/NcLoadingIcon'
const versionHash = loadState('core', 'versionHash', '')
-export default {
+export default defineComponent({
name: 'AccountMenuEntry',
components: {
@@ -55,11 +57,11 @@ export default {
},
active: {
type: Boolean,
- required: true,
+ default: false,
},
icon: {
type: String,
- required: true,
+ default: '',
},
},
@@ -76,11 +78,17 @@ export default {
},
methods: {
- handleClick() {
- this.loading = true
+ onClick(e: MouseEvent) {
+ this.$emit('click', e)
+
+ // Allow to not show the loading indicator
+ // in case the click event was already handled
+ if (!e.defaultPrevented) {
+ this.loading = true
+ }
},
},
-}
+})
</script>
<style lang="scss" scoped>
@@ -96,6 +104,12 @@ export default {
}
}
+ &__loading {
+ height: 20px;
+ width: 20px;
+ margin: calc((var(--default-clickable-area) - 20px) / 2); // 20px icon size
+ }
+
:deep(.list-item-content__main) {
width: fit-content;
}
diff --git a/core/src/components/AppMenuIcon.vue b/core/src/components/AppMenuIcon.vue
index f2cee75e644..1b0d48daf8c 100644
--- a/core/src/components/AppMenuIcon.vue
+++ b/core/src/components/AppMenuIcon.vue
@@ -14,24 +14,25 @@
</template>
<script setup lang="ts">
-import type { INavigationEntry } from '../types/navigation'
+import type { INavigationEntry } from '../types/navigation.ts'
+
import { n } from '@nextcloud/l10n'
import { computed } from 'vue'
-
-import IconDot from 'vue-material-design-icons/Circle.vue'
+import IconDot from 'vue-material-design-icons/CircleOutline.vue'
const props = defineProps<{
app: INavigationEntry
}>()
-const ariaHidden = computed(() => String(props.app.unread > 0))
+// only hide if there are no unread notifications
+const ariaHidden = computed(() => !props.app.unread ? 'true' : undefined)
const ariaLabel = computed(() => {
- if (ariaHidden.value === 'true') {
- return ''
+ if (!props.app.unread) {
+ return undefined
}
- return props.app.name
- + (props.app.unread > 0 ? ` (${n('core', '{count} notification', '{count} notifications', props.app.unread, { count: props.app.unread })})` : '')
+
+ return `${props.app.name} (${n('core', '{count} notification', '{count} notifications', props.app.unread, { count: props.app.unread })})`
})
</script>
@@ -51,6 +52,7 @@ $unread-indicator-size: 10px;
height: $icon-size;
width: $icon-size;
filter: var(--background-image-invert-if-bright);
+ mask: var(--header-menu-icon-mask);
}
&__unread {
diff --git a/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue b/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue
index 4a8640f38a8..413806c7089 100644
--- a/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue
+++ b/core/src/components/PublicPageMenu/PublicPageMenuEntry.vue
@@ -11,22 +11,24 @@
role="presentation"
@click="$emit('click')">
<template #icon>
- <div role="presentation" :class="['icon', icon, 'public-page-menu-entry__icon']" />
+ <slot v-if="$scopedSlots.icon" name="icon" />
+ <div v-else role="presentation" :class="['icon', icon, 'public-page-menu-entry__icon']" />
</template>
</NcListItem>
</template>
<script setup lang="ts">
-import NcListItem from '@nextcloud/vue/components/NcListItem'
import { onMounted } from 'vue'
+import NcListItem from '@nextcloud/vue/components/NcListItem'
+
const props = defineProps<{
/** Only emit click event but do not open href */
clickOnly?: boolean
// menu entry props
id: string
label: string
- icon: string
+ icon?: string
href: string
details?: string
}>()
diff --git a/core/src/components/UnifiedSearch/UnifiedSearchLocalSearchBar.vue b/core/src/components/UnifiedSearch/UnifiedSearchLocalSearchBar.vue
index 1860c54e1ff..171eada8a06 100644
--- a/core/src/components/UnifiedSearch/UnifiedSearchLocalSearchBar.vue
+++ b/core/src/components/UnifiedSearch/UnifiedSearchLocalSearchBar.vue
@@ -32,7 +32,7 @@
{{ t('core', 'Search everywhere') }}
</template>
<template #icon>
- <NcIconSvgWrapper :path="mdiCloudSearch" />
+ <NcIconSvgWrapper :path="mdiCloudSearchOutline" />
</template>
</NcButton>
</div>
@@ -41,7 +41,7 @@
<script lang="ts" setup>
import type { ComponentPublicInstance } from 'vue'
-import { mdiCloudSearch, mdiClose } from '@mdi/js'
+import { mdiCloudSearchOutline, mdiClose } from '@mdi/js'
import { translate as t } from '@nextcloud/l10n'
import { useIsMobile } from '@nextcloud/vue/composables/useIsMobile'
import { useElementSize } from '@vueuse/core'
diff --git a/core/src/components/UnifiedSearch/UnifiedSearchModal.vue b/core/src/components/UnifiedSearch/UnifiedSearchModal.vue
index 6327d0e4d3d..b21c65301c4 100644
--- a/core/src/components/UnifiedSearch/UnifiedSearchModal.vue
+++ b/core/src/components/UnifiedSearch/UnifiedSearchModal.vue
@@ -129,7 +129,7 @@
v-bind="result" />
</ul>
<div class="result-footer">
- <NcButton type="tertiary-no-background" @click="loadMoreResultsForProvider(providerResult)">
+ <NcButton v-if="providerResult.results.length === providerResult.limit" type="tertiary-no-background" @click="loadMoreResultsForProvider(providerResult)">
{{ t('core', 'Load more results') }}
<template #icon>
<IconDotsHorizontal :size="20" />
@@ -159,8 +159,8 @@ import debounce from 'debounce'
import { unifiedSearchLogger } from '../../logger'
import IconArrowRight from 'vue-material-design-icons/ArrowRight.vue'
-import IconAccountGroup from 'vue-material-design-icons/AccountGroup.vue'
-import IconCalendarRange from 'vue-material-design-icons/CalendarRange.vue'
+import IconAccountGroup from 'vue-material-design-icons/AccountGroupOutline.vue'
+import IconCalendarRange from 'vue-material-design-icons/CalendarRangeOutline.vue'
import IconDotsHorizontal from 'vue-material-design-icons/DotsHorizontal.vue'
import IconFilter from 'vue-material-design-icons/Filter.vue'
import IconListBox from 'vue-material-design-icons/ListBox.vue'
@@ -252,11 +252,10 @@ export default defineComponent({
providerResultLimit: 5,
dateFilter: { id: 'date', type: 'date', text: '', startFrom: null, endAt: null },
personFilter: { id: 'person', type: 'person', name: '' },
- dateFilterIsApplied: false,
- personFilterIsApplied: false,
filteredProviders: [],
searching: false,
searchQuery: '',
+ lastSearchQuery: '',
placessearchTerm: '',
dateTimeFilter: null,
filters: [],
@@ -330,7 +329,13 @@ export default defineComponent({
query: {
immediate: true,
handler() {
- this.searchQuery = this.query.trim()
+ this.searchQuery = this.query
+ },
+ },
+
+ searchQuery: {
+ handler() {
+ this.$emit('update:query', this.searchQuery)
},
},
},
@@ -362,17 +367,23 @@ export default defineComponent({
this.$refs.searchInput?.focus()
})
},
- find(query: string) {
+ find(query: string, providersToSearchOverride = null) {
if (query.length === 0) {
this.results = []
this.searching = false
return
}
+ // Reset the provider result limit when performing a new search
+ if (query !== this.lastSearchQuery) {
+ this.providerResultLimit = 5
+ }
+ this.lastSearchQuery = query
+
this.searching = true
const newResults = []
- const providersToSearch = this.filteredProviders.length > 0 ? this.filteredProviders : this.providers
- const searchProvider = (provider, filters) => {
+ const providersToSearch = providersToSearchOverride || (this.filteredProviders.length > 0 ? this.filteredProviders : this.providers)
+ const searchProvider = (provider) => {
const params = {
type: provider.searchFrom ?? provider.id,
query,
@@ -382,18 +393,25 @@ export default defineComponent({
// This block of filter checks should be dynamic somehow and should be handled in
// nextcloud/search lib
- if (filters.dateFilterIsApplied) {
- if (provider.filters?.since && provider.filters?.until) {
- params.since = this.dateFilter.startFrom
- params.until = this.dateFilter.endAt
- }
- }
+ const activeFilters = this.filters.filter(filter => {
+ return filter.type !== 'provider' && this.providerIsCompatibleWithFilters(provider, [filter.type])
+ })
- if (filters.personFilterIsApplied) {
- if (provider.filters?.person) {
- params.person = this.personFilter.user
+ activeFilters.forEach(filter => {
+ switch (filter.type) {
+ case 'date':
+ if (provider.filters?.since && provider.filters?.until) {
+ params.since = this.dateFilter.startFrom
+ params.until = this.dateFilter.endAt
+ }
+ break
+ case 'person':
+ if (provider.filters?.person) {
+ params.person = this.personFilter.user
+ }
+ break
}
- }
+ })
if (this.providerResultLimit > 5) {
params.limit = this.providerResultLimit
@@ -404,13 +422,9 @@ export default defineComponent({
request().then((response) => {
newResults.push({
- id: provider.id,
- appId: provider.appId,
- searchFrom: provider.searchFrom,
- icon: provider.icon,
- name: provider.name,
- inAppSearch: provider.inAppSearch,
+ ...provider,
results: response.data.ocs.data.entries,
+ limit: params.limit ?? 5,
})
unifiedSearchLogger.debug('Unified search results:', { results: this.results, newResults })
@@ -419,12 +433,8 @@ export default defineComponent({
this.searching = false
})
}
- providersToSearch.forEach(provider => {
- const dateFilterIsApplied = this.dateFilterIsApplied
- const personFilterIsApplied = this.personFilterIsApplied
- searchProvider(provider, { dateFilterIsApplied, personFilterIsApplied })
- })
+ providersToSearch.forEach(searchProvider)
},
updateResults(newResults) {
let updatedResults = [...this.results]
@@ -482,7 +492,7 @@ export default defineComponent({
})
},
applyPersonFilter(person) {
- this.personFilterIsApplied = true
+
const existingPersonFilter = this.filters.findIndex(filter => filter.id === person.id)
if (existingPersonFilter === -1) {
this.personFilter.id = person.id
@@ -504,15 +514,7 @@ export default defineComponent({
},
async loadMoreResultsForProvider(provider) {
this.providerResultLimit += 5
- // If load more result for filter, remove other filters
- this.filters = this.filters.filter(filter => filter.id === provider.id)
- this.filteredProviders = this.filteredProviders.filter(filteredProvider => filteredProvider.id === provider.id)
- // Plugin filters may have extra parameters, so we need to keep them
- // See method handlePluginFilter for more details
- if (this.filteredProviders.length > 0 && this.filteredProviders[0].isPluginFilter) {
- provider = this.filteredProviders[0]
- }
- this.addProviderFilter(provider, true)
+ this.find(this.searchQuery, [provider])
},
addProviderFilter(providerFilter, loadMoreResultsForProvider = false) {
unifiedSearchLogger.debug('Applying provider filter', { providerFilter, loadMoreResultsForProvider })
@@ -556,14 +558,10 @@ export default defineComponent({
unifiedSearchLogger.debug('Search filters (recently removed)', { filters: this.filters })
} else {
+ // Remove non provider filters such as date and person filters
for (let i = 0; i < this.filters.length; i++) {
- // Remove date and person filter
- if (this.filters[i].id === 'date' || this.filters[i].id === filter.id) {
- this.dateFilterIsApplied = false
+ if (this.filters[i].id === filter.id) {
this.filters.splice(i, 1)
- if (filter.type === 'person') {
- this.personFilterIsApplied = false
- }
this.enableAllProviders()
break
}
@@ -602,7 +600,7 @@ export default defineComponent({
} else {
this.filters.push(this.dateFilter)
}
- this.dateFilterIsApplied = true
+
this.providers.forEach(async (provider, index) => {
this.providers[index].disabled = !(await this.providerIsCompatibleWithFilters(provider, ['since', 'until']))
})
diff --git a/core/src/components/login/PasswordLessLoginForm.vue b/core/src/components/login/PasswordLessLoginForm.vue
index bbca2ebf31d..bc4d25bf70f 100644
--- a/core/src/components/login/PasswordLessLoginForm.vue
+++ b/core/src/components/login/PasswordLessLoginForm.vue
@@ -57,7 +57,7 @@ import {
import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent'
import NcTextField from '@nextcloud/vue/components/NcTextField'
-import InformationIcon from 'vue-material-design-icons/Information.vue'
+import InformationIcon from 'vue-material-design-icons/InformationOutline.vue'
import LoginButton from './LoginButton.vue'
import LockOpenIcon from 'vue-material-design-icons/LockOpen.vue'
import logger from '../../logger'
diff --git a/core/src/components/setup/RecommendedApps.vue b/core/src/components/setup/RecommendedApps.vue
index aaad6d93476..f2120c28402 100644
--- a/core/src/components/setup/RecommendedApps.vue
+++ b/core/src/components/setup/RecommendedApps.vue
@@ -4,7 +4,7 @@
-->
<template>
- <div class="guest-box">
+ <div class="guest-box" data-cy-setup-recommended-apps>
<h2>{{ t('core', 'Recommended apps') }}</h2>
<p v-if="loadingApps" class="loading text-center">
{{ t('core', 'Loading apps …') }}
@@ -38,15 +38,16 @@
<div class="dialog-row">
<NcButton v-if="showInstallButton && !installingApps"
- type="tertiary"
- role="link"
- :href="defaultPageUrl">
+ data-cy-setup-recommended-apps-skip
+ :href="defaultPageUrl"
+ variant="tertiary">
{{ t('core', 'Skip') }}
</NcButton>
<NcButton v-if="showInstallButton"
- type="primary"
+ data-cy-setup-recommended-apps-install
:disabled="installingApps || !isAnyAppSelected"
+ variant="primary"
@click.stop.prevent="installApps">
{{ installingApps ? t('core', 'Installing apps …') : t('core', 'Install recommended apps') }}
</NcButton>