diff options
Diffstat (limited to 'core/src/components/UnifiedSearch')
7 files changed, 244 insertions, 175 deletions
diff --git a/core/src/components/UnifiedSearch/CustomDateRangeModal.vue b/core/src/components/UnifiedSearch/CustomDateRangeModal.vue index 332a4286863..d86192d156e 100644 --- a/core/src/components/UnifiedSearch/CustomDateRangeModal.vue +++ b/core/src/components/UnifiedSearch/CustomDateRangeModal.vue @@ -37,9 +37,9 @@ </template> <script> -import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' -import NcDateTimePicker from '@nextcloud/vue/dist/Components/NcDateTimePickerNative.js' -import NcModal from '@nextcloud/vue/dist/Components/NcModal.js' +import NcButton from '@nextcloud/vue/components/NcButton' +import NcDateTimePicker from '@nextcloud/vue/components/NcDateTimePickerNative' +import NcModal from '@nextcloud/vue/components/NcModal' import CalendarRangeIcon from 'vue-material-design-icons/CalendarRange.vue' export default { diff --git a/core/src/components/UnifiedSearch/LegacySearchResult.vue b/core/src/components/UnifiedSearch/LegacySearchResult.vue index 29553dfc661..4592adf08c9 100644 --- a/core/src/components/UnifiedSearch/LegacySearchResult.vue +++ b/core/src/components/UnifiedSearch/LegacySearchResult.vue @@ -42,7 +42,7 @@ </template> <script> -import NcHighlight from '@nextcloud/vue/dist/Components/NcHighlight.js' +import NcHighlight from '@nextcloud/vue/components/NcHighlight' export default { name: 'LegacySearchResult', @@ -219,7 +219,7 @@ $margin: 10px; flex-wrap: wrap; // Set to minimum and gro from it min-width: 0; - padding-left: $margin; + padding-inline-start: $margin; } &-line-one, diff --git a/core/src/components/UnifiedSearch/SearchFilterChip.vue b/core/src/components/UnifiedSearch/SearchFilterChip.vue index f3945b78153..e08ddd58a4b 100644 --- a/core/src/components/UnifiedSearch/SearchFilterChip.vue +++ b/core/src/components/UnifiedSearch/SearchFilterChip.vue @@ -54,7 +54,7 @@ export default { .icon { display: flex; align-items: center; - padding-right: 5px; + padding-inline-end: 5px; img { width: 20px; diff --git a/core/src/components/UnifiedSearch/SearchResult.vue b/core/src/components/UnifiedSearch/SearchResult.vue index 93e104f1a20..4f33fbd54cc 100644 --- a/core/src/components/UnifiedSearch/SearchResult.vue +++ b/core/src/components/UnifiedSearch/SearchResult.vue @@ -3,18 +3,18 @@ - SPDX-License-Identifier: AGPL-3.0-or-later --> <template> - <NcListItem class="result-items__item" + <NcListItem class="result-item" :name="title" :bold="false" :href="resourceUrl" target="_self"> <template #icon> <div aria-hidden="true" - class="result-items__item-icon" + class="result-item__icon" :class="{ - 'result-items__item-icon--rounded': rounded, - 'result-items__item-icon--no-preview': !isValidIconOrPreviewUrl(thumbnailUrl), - 'result-items__item-icon--with-thumbnail': isValidIconOrPreviewUrl(thumbnailUrl), + 'result-item__icon--rounded': rounded, + 'result-item__icon--no-preview': !isValidIconOrPreviewUrl(thumbnailUrl), + 'result-item__icon--with-thumbnail': isValidIconOrPreviewUrl(thumbnailUrl), [icon]: !isValidIconOrPreviewUrl(icon), }" :style="{ @@ -32,7 +32,7 @@ </template> <script> -import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js' +import NcListItem from '@nextcloud/vue/components/NcListItem' export default { name: 'SearchResult', @@ -101,73 +101,59 @@ export default { </script> <style lang="scss" scoped> -@use "sass:math"; -$clickable-area: 44px; -$margin: 10px; - -.result-items { - &__item { - - ::v-deep a { - border-radius: 12px; - border: 2px solid transparent; - border-radius: var(--border-radius-large) !important; - - &--focused { - background-color: var(--color-background-hover); - } - - &:active, - &:hover, - &:focus { - background-color: var(--color-background-hover); - border: 2px solid var(--color-border-maxcontrast); - } - - * { - cursor: pointer; - } - - } - - &-icon { - overflow: hidden; - width: $clickable-area; - height: $clickable-area; - border-radius: var(--border-radius); - background-repeat: no-repeat; - background-position: center center; - background-size: 32px; - - &--rounded { - border-radius: math.div($clickable-area, 2); - } +.result-item { + :deep(a) { + border: 2px solid transparent; + border-radius: var(--border-radius-large) !important; + + &:active, + &:hover, + &:focus { + background-color: var(--color-background-hover); + border: 2px solid var(--color-border-maxcontrast); + } - &--no-preview { - background-size: 32px; - } + * { + cursor: pointer; + } + } + + &__icon { + overflow: hidden; + width: var(--default-clickable-area); + height: var(--default-clickable-area); + border-radius: var(--border-radius); + background-repeat: no-repeat; + background-position: center center; + background-size: 32px; + + &--rounded { + border-radius: calc(var(--default-clickable-area) / 2); + } - &--with-thumbnail { - background-size: cover; - } + &--no-preview { + background-size: 32px; + } - &--with-thumbnail:not(&--rounded) { - // compensate for border - max-width: $clickable-area - 2px; - max-height: $clickable-area - 2px; - border: 1px solid var(--color-border); - } + &--with-thumbnail { + background-size: cover; + } - img { - // Make sure to keep ratio - width: 100%; - height: 100%; + &--with-thumbnail:not(#{&}--rounded) { + border: 1px solid var(--color-border); + // compensate for border + max-height: calc(var(--default-clickable-area) - 2px); + max-width: calc(var(--default-clickable-area) - 2px); + } - object-fit: cover; - object-position: center; - } - } + img { + // Make sure to keep ratio + width: 100%; + height: 100%; - } + object-fit: cover; + object-position: center; + } + } } </style> diff --git a/core/src/components/UnifiedSearch/SearchableList.vue b/core/src/components/UnifiedSearch/SearchableList.vue index b2081c2c5ee..d7abb6ffdbb 100644 --- a/core/src/components/UnifiedSearch/SearchableList.vue +++ b/core/src/components/UnifiedSearch/SearchableList.vue @@ -17,7 +17,7 @@ :show-trailing-button="searchTerm !== ''" @update:value="searchTermChanged" @trailing-button-click="clearSearch"> - <Magnify :size="20" /> + <IconMagnify :size="20" /> </NcTextField> <ul v-if="filteredList.length > 0" class="searchable-list__list"> <li v-for="element in filteredList" @@ -42,7 +42,7 @@ <div v-else class="searchable-list__empty-content"> <NcEmptyContent :name="emptyContentText"> <template #icon> - <AlertCircleOutline /> + <IconAlertCircleOutline /> </template> </NcEmptyContent> </div> @@ -51,22 +51,26 @@ </template> <script> -import { NcPopover, NcTextField, NcAvatar, NcEmptyContent, NcButton } from '@nextcloud/vue' +import NcAvatar from '@nextcloud/vue/components/NcAvatar' +import NcButton from '@nextcloud/vue/components/NcButton' +import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent' +import NcPopover from '@nextcloud/vue/components/NcPopover' +import NcTextField from '@nextcloud/vue/components/NcTextField' -import AlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue' -import Magnify from 'vue-material-design-icons/Magnify.vue' +import IconAlertCircleOutline from 'vue-material-design-icons/AlertCircleOutline.vue' +import IconMagnify from 'vue-material-design-icons/Magnify.vue' export default { name: 'SearchableList', components: { - NcPopover, - NcTextField, - Magnify, - AlertCircleOutline, + IconMagnify, + IconAlertCircleOutline, NcAvatar, - NcEmptyContent, NcButton, + NcEmptyContent, + NcPopover, + NcTextField, }, props: { diff --git a/core/src/components/UnifiedSearch/UnifiedSearchLocalSearchBar.vue b/core/src/components/UnifiedSearch/UnifiedSearchLocalSearchBar.vue index d68466ea91a..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,15 +41,15 @@ <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/dist/Composables/useIsMobile.js' +import { useIsMobile } from '@nextcloud/vue/composables/useIsMobile' +import { useElementSize } from '@vueuse/core' import { computed, ref, watchEffect } from 'vue' -import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' -import NcIconSvgWrapper from '@nextcloud/vue/dist/Components/NcIconSvgWrapper.js' -import NcInputField from '@nextcloud/vue/dist/Components/NcInputField.js' -import { useElementSize } from '@vueuse/core' +import NcButton from '@nextcloud/vue/components/NcButton' +import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper' +import NcInputField from '@nextcloud/vue/components/NcInputField' const props = defineProps<{ query: string, @@ -93,7 +93,6 @@ function clearAndCloseSearch() { <style scoped lang="scss"> .local-unified-search { --local-search-width: min(calc(250px + v-bind('searchGlobalButtonCSSWidth')), 95vw); - box-sizing: border-box; position: relative; height: var(--header-height); @@ -124,7 +123,7 @@ function clearAndCloseSearch() { // this can break at any time the component library changes :deep(input) { // search global width + close button width - padding-inline-end: calc(v-bind('searchGlobalButtonWidth') + var(--default-clickable-area)); + padding-inline-end: calc(v-bind('searchGlobalButtonCSSWidth') + var(--default-clickable-area)); } } } @@ -133,8 +132,8 @@ function clearAndCloseSearch() { transition: width var(--animation-quick) linear; } -// Make the position absolut during the transition -// this is needed to "hide" the button begind it +// Make the position absolute during the transition +// this is needed to "hide" the button behind it .v-leave-active { position: absolute !important; } @@ -142,7 +141,7 @@ function clearAndCloseSearch() { .v-enter, .v-leave-to { &.local-unified-search { - // Start with only the overlayed button + // Start with only the overlay button --local-search-width: var(--clickable-area-large); } } @@ -154,13 +153,13 @@ function clearAndCloseSearch() { padding-inline: var(--default-grid-baseline); } - // when open we need to position it absolut to allow overlay the full bar + // when open we need to position it absolute to allow overlay the full bar :global(.unified-search-menu:has(.local-unified-search--open)) { position: absolute !important; inset-inline: 0; } // Hide all other entries, especially the user menu as it might leak pixels - :global(.header-right:has(.local-unified-search--open) > :not(.unified-search-menu)) { + :global(.header-end:has(.local-unified-search--open) > :not(.unified-search-menu)) { display: none; } } diff --git a/core/src/components/UnifiedSearch/UnifiedSearchModal.vue b/core/src/components/UnifiedSearch/UnifiedSearchModal.vue index 75009ee9be5..e59058bc0f0 100644 --- a/core/src/components/UnifiedSearch/UnifiedSearchModal.vue +++ b/core/src/components/UnifiedSearch/UnifiedSearchModal.vue @@ -9,6 +9,7 @@ dialog-classes="unified-search-modal" :name="t('core', 'Unified search')" :open="open" + size="normal" @update:open="onUpdateOpen"> <!-- Modal for picking custom time range --> <CustomDateRangeModal :is-open="showDateRangeModal" @@ -33,6 +34,7 @@ provider.id concatenated to provider.name is used to create the item id, if same then, there should be an issue. --> <NcActionButton v-for="provider in providers" :key="`${provider.id}-${provider.name.replace(/\s/g, '')}`" + :disabled="provider.disabled" @click="addProviderFilter(provider)"> <template #icon> <img :src="provider.icon" class="filter-button__icon" alt=""> @@ -84,6 +86,13 @@ <IconFilter :size="20" /> </template> </NcButton> + <NcCheckboxRadioSwitch v-if="hasExternalResources" + v-model="searchExternalResources" + type="switch" + class="unified-search-modal__search-external-resources" + :class="{'unified-search-modal__search-external-resources--aligned': localSearch}"> + {{ t('core', 'Search connected services') }} + </NcCheckboxRadioSwitch> </div> <div class="unified-search-modal__filters-applied"> <FilterChip v-for="filter in filters" @@ -114,10 +123,12 @@ </div> <div v-else class="unified-search-modal__results"> - <h3 class="hidden-visually">{{ t('core', 'Results') }}</h3> + <h3 class="hidden-visually"> + {{ t('core', 'Results') }} + </h3> <div v-for="providerResult in results" :key="providerResult.id" class="result"> <h4 :id="`unified-search-result-${providerResult.id}`" class="result-title"> - {{ providerResult.provider }} + {{ providerResult.name }} </h4> <ul class="result-items" :aria-labelledby="`unified-search-result-${providerResult.id}`"> <SearchResult v-for="(result, index) in providerResult.results" @@ -125,14 +136,14 @@ v-bind="result" /> </ul> <div class="result-footer"> - <NcButton type="tertiary-no-background" @click="loadMoreResultsForProvider(providerResult.id)"> + <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" /> </template> </NcButton> <NcButton v-if="providerResult.inAppSearch" alignment="end-reverse" type="tertiary-no-background"> - {{ t('core', 'Search in') }} {{ providerResult.provider }} + {{ t('core', 'Search in') }} {{ providerResult.name }} <template #icon> <IconArrowRight :size="20" /> </template> @@ -155,19 +166,20 @@ 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' import IconMagnify from 'vue-material-design-icons/Magnify.vue' -import NcActions from '@nextcloud/vue/dist/Components/NcActions.js' -import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js' -import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js' -import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' -import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js' -import NcInputField from '@nextcloud/vue/dist/Components/NcInputField.js' -import NcDialog from '@nextcloud/vue/dist/Components/NcDialog.js' +import NcActions from '@nextcloud/vue/components/NcActions' +import NcActionButton from '@nextcloud/vue/components/NcActionButton' +import NcAvatar from '@nextcloud/vue/components/NcAvatar' +import NcButton from '@nextcloud/vue/components/NcButton' +import NcEmptyContent from '@nextcloud/vue/components/NcEmptyContent' +import NcInputField from '@nextcloud/vue/components/NcInputField' +import NcDialog from '@nextcloud/vue/components/NcDialog' +import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch' import CustomDateRangeModal from './CustomDateRangeModal.vue' import FilterChip from './SearchFilterChip.vue' @@ -194,6 +206,7 @@ export default defineComponent({ NcEmptyContent, NcDialog, NcInputField, + NcCheckboxRadioSwitch, SearchableList, SearchResult, }, @@ -248,11 +261,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: [], @@ -260,6 +272,8 @@ export default defineComponent({ contacts: [], showDateRangeModal: false, internalIsVisible: this.open, + initialized: false, + searchExternalResources: false, } }, @@ -297,38 +311,57 @@ export default defineComponent({ debouncedFilterContacts() { return debounce(this.filterContacts, 300) }, + + hasExternalResources() { + return this.providers.some(provider => provider.isExternalProvider) + }, }, watch: { open() { // Load results when opened with already filled query - if (this.open && this.searchQuery) { - this.find(this.searchQuery) + if (this.open) { + this.focusInput() + if (!this.initialized) { + Promise.all([getProviders(), getContacts({ searchTerm: '' })]) + .then(([providers, contacts]) => { + this.providers = this.groupProvidersByApp([...providers, ...this.externalFilters]) + this.contacts = this.mapContacts(contacts) + unifiedSearchLogger.debug('Search providers and contacts initialized:', { providers: this.providers, contacts: this.contacts }) + this.initialized = true + }) + .catch((error) => { + unifiedSearchLogger.error(error) + }) + } + if (this.searchQuery) { + this.find(this.searchQuery) + } } }, query: { immediate: true, handler() { - this.searchQuery = this.query.trim() + this.searchQuery = this.query + }, + }, + + searchQuery: { + handler() { + this.$emit('update:query', this.searchQuery) + }, + }, + + searchExternalResources() { + if (this.searchQuery) { + this.find(this.searchQuery) } }, }, mounted() { subscribe('nextcloud:unified-search:add-filter', this.handlePluginFilter) - getProviders().then((providers) => { - this.providers = providers - this.externalFilters.forEach(filter => { - this.providers.push(filter) - }) - this.providers = this.groupProvidersByApp(this.providers) - unifiedSearchLogger.debug('Search providers', { providers: this.providers }) - }) - getContacts({ searchTerm: '' }).then((contacts) => { - this.contacts = this.mapContacts(contacts) - unifiedSearchLogger.debug('Contacts', { contacts: this.contacts }) - }) }, methods: { /** @@ -349,56 +382,77 @@ export default defineComponent({ this.$emit('update:query', this.searchQuery) this.$emit('update:open', false) }, - - find(query: string) { + focusInput() { + this.$nextTick(() => { + this.$refs.searchInput?.focus() + }) + }, + 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.id, + type: provider.searchFrom ?? provider.id, query, cursor: null, extraQueries: provider.extraParams, } - if (filters.dateFilterIsApplied) { - if (provider.filters?.since && provider.filters?.until) { - params.since = this.dateFilter.startFrom - params.until = this.dateFilter.endAt - } else { - // Date filter is applied but provider does not support it, no need to search provider - return - } - } + // This block of filter checks should be dynamic somehow and should be handled in + // nextcloud/search lib + 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 - } else { - // Person filter is applied but provider does not support it, no need to search provider - return + 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 + unifiedSearchLogger.debug('Limiting search to', params.limit) + } + + const shouldSkipSearch = !this.searchExternalResources && provider.isExternalProvider + const wasManuallySelected = this.filteredProviders.some(filteredProvider => filteredProvider.id === provider.id) + // if the provider is an external resource and the user has not manually selected it, skip the search + if (shouldSkipSearch && !wasManuallySelected) { + this.searching = false + return } const request = unifiedSearch(params).request request().then((response) => { newResults.push({ - id: provider.id, - provider: 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 }) @@ -407,12 +461,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] @@ -470,7 +520,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 @@ -483,19 +533,27 @@ export default defineComponent({ this.filters[existingPersonFilter].name = person.displayName } + this.providers.forEach(async (provider, index) => { + this.providers[index].disabled = !(await this.providerIsCompatibleWithFilters(provider, ['person'])) + }) + this.debouncedFind(this.searchQuery) unifiedSearchLogger.debug('Person filter applied', { person }) }, - loadMoreResultsForProvider(providerId) { + async loadMoreResultsForProvider(provider) { this.providerResultLimit += 5 - this.filters = this.filters.filter(filter => filter.type !== 'provider') - const provider = this.providers.find(provider => provider.id === providerId) - this.addProviderFilter(provider, true) + this.find(this.searchQuery, [provider]) }, addProviderFilter(providerFilter, loadMoreResultsForProvider = false) { + unifiedSearchLogger.debug('Applying provider filter', { providerFilter, loadMoreResultsForProvider }) if (!providerFilter.id) return if (providerFilter.isPluginFilter) { - providerFilter.callback() + // There is no way to know what should go into the callback currently + // Here we are passing isProviderFilterApplied (boolean) which is a flag sent to the plugin + // This is sent to the plugin so that depending on whether the filter is applied or not, the plugin can decide what to do + // TODO : In nextcloud/search, this should be a proper interface that the plugin can implement + const isProviderFilterApplied = this.filteredProviders.some(provider => provider.id === providerFilter.id) + providerFilter.callback(!isProviderFilterApplied) } this.providerResultLimit = loadMoreResultsForProvider ? this.providerResultLimit : 5 this.providerActionMenuIsOpen = false @@ -508,11 +566,8 @@ export default defineComponent({ this.filters = this.syncProviderFilters(this.filters, this.filteredProviders) } this.filteredProviders.push({ - id: providerFilter.id, - name: providerFilter.name, - icon: providerFilter.icon, + ...providerFilter, type: providerFilter.type || 'provider', - filters: providerFilter.filters, isPluginFilter: providerFilter.isPluginFilter || false, }) this.filters = this.syncProviderFilters(this.filters, this.filteredProviders) @@ -531,14 +586,11 @@ 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 } } @@ -576,7 +628,10 @@ 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'])) + }) this.debouncedFind(this.searchQuery) }, applyQuickDateRange(range) { @@ -633,6 +688,7 @@ export default defineComponent({ this.updateDateFilter() }, handlePluginFilter(addFilterEvent) { + unifiedSearchLogger.debug('Handling plugin filter', { addFilterEvent }) for (let i = 0; i < this.filteredProviders.length; i++) { const provider = this.filteredProviders[i] if (provider.id === addFilterEvent.id) { @@ -667,6 +723,14 @@ export default defineComponent({ return flattenedArray }, + async providerIsCompatibleWithFilters(provider, filterIds) { + return filterIds.every(filterId => provider.filters?.[filterId] !== undefined) + }, + async enableAllProviders() { + this.providers.forEach(async (_, index) => { + this.providers[index].disabled = false + }) + }, }, }) </script> @@ -706,6 +770,21 @@ export default defineComponent({ padding-top: 4px; } + &__search-external-resources { + :deep(span.checkbox-content) { + padding-top: 0; + padding-bottom: 0; + } + + :deep(.checkbox-content__icon) { + margin: auto !important; + } + + &--aligned { + margin-inline-start: auto; + } + } + &__filters-applied { padding-top: 4px; display: flex; @@ -715,7 +794,8 @@ export default defineComponent({ &__no-content { display: flex; align-items: center; - height: 100%; + margin-top: 0.5em; + height: 70%; } &__results { |