2727 :label =" t('core', 'Search apps, files, tags, messages') + '...'"
2828 @update:value =" debouncedFind" />
2929 <div class =" unified-search-modal__filters" data-cy-unified-search-filters >
30- <NcActions v-model :open =" providerActionMenuIsOpen" :menu-name =" t('core', 'Places')" data-cy-unified-search-filter =" places" >
30+ <NcActions :open.sync =" providerActionMenuIsOpen" :menu-name =" t('core', 'Places')" data-cy-unified-search-filter =" places" >
3131 <template #icon >
3232 <IconListBox :size =" 20" />
3333 </template >
4343 {{ provider.name }}
4444 </NcActionButton >
4545 </NcActions >
46- <NcActions v-model :open =" dateActionMenuIsOpen" :menu-name =" t('core', 'Date')" data-cy-unified-search-filter =" date" >
46+ <NcActions :open.sync =" dateActionMenuIsOpen" :menu-name =" t('core', 'Date')" data-cy-unified-search-filter =" date" >
4747 <template #icon >
4848 <IconCalendarRange :size =" 20" />
4949 </template >
120120 <h3 class =" hidden-visually" >
121121 {{ t('core', 'Results') }}
122122 </h3 >
123- <div v-for =" providerResult in results" :key =" providerResult.id" class =" result" >
123+ <!-- Filtered results section -->
124+ <div v-for =" providerResult in filteredResults" :key =" providerResult.id" class =" result" >
124125 <h4 :id =" `unified-search-result-${providerResult.id}`" class =" result-title" >
125126 {{ providerResult.name }}
126127 </h4 >
144145 </NcButton >
145146 </div >
146147 </div >
148+ <!-- Unfiltered results section -->
149+ <template v-if =" unfilteredResults .length > 0 " >
150+ <div class =" unified-search-modal__unfiltered-header" >
151+ <span class =" unified-search-modal__unfiltered-label" >{{ t('core', 'Partial matches') }}</span >
152+ </div >
153+ <div v-for =" providerResult in unfilteredResults" :key =" `unfiltered-${providerResult.id}`" class =" result result--unfiltered" >
154+ <h4 :id =" `unified-search-result-unfiltered-${providerResult.id}`" class =" result-title" >
155+ {{ providerResult.name }}
156+ </h4 >
157+ <ul class =" result-items" :aria-labelledby =" `unified-search-result-unfiltered-${providerResult.id}`" >
158+ <SearchResult v-for =" (result, index) in providerResult.results"
159+ :key =" index"
160+ v-bind =" result" />
161+ </ul >
162+ <div class =" result-footer" >
163+ <NcButton v-if =" providerResult.results.length === providerResult.limit" variant =" tertiary-no-background" @click =" loadMoreResultsForProvider(providerResult)" >
164+ {{ t('core', 'Load more results') }}
165+ <template #icon >
166+ <IconDotsHorizontal :size =" 20" />
167+ </template >
168+ </NcButton >
169+ <NcButton v-if =" providerResult.inAppSearch" alignment =" end-reverse" variant =" tertiary-no-background" >
170+ {{ t('core', 'Search in') }} {{ providerResult.name }}
171+ <template #icon >
172+ <IconArrowRight :size =" 20" />
173+ </template >
174+ </NcButton >
175+ </div >
176+ </div >
177+ </template >
147178 </div >
148179 </NcDialog >
149180</template >
@@ -321,6 +352,50 @@ export default defineComponent({
321352 debouncedFilterContacts() {
322353 return debounce (this .filterContacts , 300 )
323354 },
355+
356+ hasContentFilters() {
357+ return this .filters .some ((filter ) => filter .type === ' date' || filter .type === ' person' )
358+ },
359+
360+ filteredResults() {
361+ const isInFolderAtRoot = (result ) => {
362+ if (result .id !== ' in-folder' ) {
363+ return false
364+ }
365+ const path = result .extraParams ?.path
366+ return ! path || path === ' /' || path === ' '
367+ }
368+
369+ if (! this .hasContentFilters ) {
370+ return this .results .filter ((result ) => ! isInFolderAtRoot (result ))
371+ }
372+ return this .results .filter ((result ) => result .supportsActiveFilters === true && ! isInFolderAtRoot (result ))
373+ },
374+
375+ filteredResultUrls() {
376+ const urls = new Set ()
377+ this .filteredResults .forEach ((provider ) => {
378+ provider .results .forEach ((entry ) => {
379+ if (entry .resourceUrl ) {
380+ urls .add (entry .resourceUrl )
381+ }
382+ })
383+ })
384+ return urls
385+ },
386+
387+ unfilteredResults() {
388+ if (! this .hasContentFilters ) {
389+ return []
390+ }
391+ return this .results
392+ .filter ((result ) => result .supportsActiveFilters === false )
393+ .map ((provider ) => ({
394+ ... provider ,
395+ results: provider .results .filter ((entry ) => ! this .filteredResultUrls .has (entry .resourceUrl )),
396+ }))
397+ .filter ((provider ) => provider .results .length > 0 )
398+ },
324399 },
325400
326401 watch: {
@@ -433,6 +508,10 @@ export default defineComponent({
433508 }
434509 })
435510
511+ const activeContentFilters = this .filters .filter (f => f .type === ' date' || f .type === ' person' )
512+ const supportedContentFilters = activeFilters .filter (f => f .type === ' date' || f .type === ' person' )
513+ const supportsActiveFilters = supportedContentFilters .length >= activeContentFilters .length
514+
436515 if (this .providerResultLimit > 5 ) {
437516 params .limit = this .providerResultLimit
438517 unifiedSearchLogger .debug (' Limiting search to' , params .limit )
@@ -445,6 +524,7 @@ export default defineComponent({
445524 ... provider ,
446525 results: response .data .ocs .data .entries ,
447526 limit: params .limit ?? 5 ,
527+ supportsActiveFilters ,
448528 })
449529
450530 unifiedSearchLogger .debug (' Unified search results:' , { results: this .results , newResults })
@@ -793,9 +873,27 @@ export default defineComponent({
793873 align-items : center ;
794874 display : flex ;
795875 }
876+
877+ & --unfiltered {
878+ opacity : 0.7 ;
879+ }
796880 }
797881
798882 }
883+
884+ & __unfiltered-header {
885+ display : flex ;
886+ flex-direction : column ;
887+ gap : 2px ;
888+ margin-block : 16px 8px ;
889+ padding-block : 12px 0 ;
890+ border-top : 1px solid var (--color-border );
891+ }
892+
893+ & __unfiltered-label {
894+ font-weight : bold ;
895+ color : var (--color-text-maxcontrast );
896+ }
799897}
800898
801899.filter-button__icon {
0 commit comments