1212 @update:is-open =" showDateRangeModal = $event" />
1313 <!-- Unified search form -->
1414 <div ref =" unifiedSearch" class =" unified-search-modal" >
15- <h1 >{{ t('core', 'Unified search') }}</h1 >
16- <NcInputField ref =" searchInput"
17- :value.sync =" searchQuery"
18- type =" text"
19- :label =" t('core', 'Search apps, files, tags, messages') + '...'"
20- @update:value =" debouncedFind" />
21- <div class =" unified-search-modal__filters" >
22- <NcActions :menu-name =" t('core', 'Apps and Settings')" :open.sync =" providerActionMenuIsOpen" >
23- <template #icon >
24- <ListBox :size =" 20" />
25- </template >
26- <NcActionButton v-for =" provider in providers" :key =" provider.id" @click =" addProviderFilter(provider)" >
15+ <div class =" unified-search-modal__header" >
16+ <h1 >{{ t('core', 'Unified search') }}</h1 >
17+ <NcInputField ref =" searchInput"
18+ :value.sync =" searchQuery"
19+ type =" text"
20+ :label =" t('core', 'Search apps, files, tags, messages') + '...'"
21+ @update:value =" debouncedFind" />
22+ <div class =" unified-search-modal__filters" >
23+ <NcActions :menu-name =" t('core', 'Apps and Settings')" :open.sync =" providerActionMenuIsOpen" >
2724 <template #icon >
28- <img :src = " provider.icon " >
25+ <ListBox :size = " 20 " / >
2926 </template >
30- {{ t('core', provider.name) }}
31- </NcActionButton >
32- </NcActions >
33- <NcActions :menu-name =" t('core', 'Date')" :open.sync =" dateActionMenuIsOpen" >
34- <template #icon >
35- <CalendarRangeIcon :size =" 20" />
36- </template >
37- <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('today')" >
38- {{ t('core', 'Today') }}
39- </NcActionButton >
40- <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('7days')" >
41- {{ t('core', 'Last 7 days') }}
42- </NcActionButton >
43- <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('30days')" >
44- {{ t('core', 'Last 30 days') }}
45- </NcActionButton >
46- <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('thisyear')" >
47- {{ t('core', 'This year') }}
48- </NcActionButton >
49- <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('lastyear')" >
50- {{ t('core', 'Last year') }}
51- </NcActionButton >
52- <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('custom')" >
53- {{ t('core', 'Custom date range') }}
54- </NcActionButton >
55- </NcActions >
56- <SearchableList :label-text =" t('core', 'Search people')"
57- :search-list =" userContacts"
58- :empty-content-text =" t('core', 'Not found')"
59- @item-selected =" applyPersonFilter" >
60- <template #trigger >
61- <NcButton >
27+ <NcActionButton v-for =" provider in providers"
28+ :key =" provider.id"
29+ @click =" addProviderFilter(provider)" >
6230 <template #icon >
63- <AccountGroup :size = " 20 " / >
31+ <img :src = " provider.icon " class = " filter-button__icon " alt = " " >
6432 </template >
65- {{ t('core', 'People') }}
66- </NcButton >
67- </template >
68- </SearchableList >
69- </div >
70- <div class =" unified-search-modal__filters-applied" >
71- <FilterChip v-for =" filter in filters"
72- :key =" filter.id"
73- :text =" filter.name ?? filter.text"
74- :pretext =" ''"
75- @delete =" removeFilter(filter)" >
76- <template #icon >
77- <NcAvatar v-if =" filter.type === 'person'"
78- :user =" filter.user"
79- :size =" 24"
80- :disable-menu =" true"
81- :show-user-status =" false"
82- :hide-favorite =" false" />
83- <CalendarRangeIcon v-else-if =" filter.type === 'date'" />
84- <img v-else :src =" filter.icon" alt =" " >
85- </template >
86- </FilterChip >
33+ {{ provider.name }}
34+ </NcActionButton >
35+ </NcActions >
36+ <NcActions :menu-name =" t('core', 'Date')" :open.sync =" dateActionMenuIsOpen" >
37+ <template #icon >
38+ <CalendarRangeIcon :size =" 20" />
39+ </template >
40+ <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('today')" >
41+ {{ t('core', 'Today') }}
42+ </NcActionButton >
43+ <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('7days')" >
44+ {{ t('core', 'Last 7 days') }}
45+ </NcActionButton >
46+ <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('30days')" >
47+ {{ t('core', 'Last 30 days') }}
48+ </NcActionButton >
49+ <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('thisyear')" >
50+ {{ t('core', 'This year') }}
51+ </NcActionButton >
52+ <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('lastyear')" >
53+ {{ t('core', 'Last year') }}
54+ </NcActionButton >
55+ <NcActionButton :close-after-click =" true" @click =" applyQuickDateRange('custom')" >
56+ {{ t('core', 'Custom date range') }}
57+ </NcActionButton >
58+ </NcActions >
59+ <SearchableList :label-text =" t('core', 'Search people')"
60+ :search-list =" userContacts"
61+ :empty-content-text =" t('core', 'Not found')"
62+ @item-selected =" applyPersonFilter" >
63+ <template #trigger >
64+ <NcButton >
65+ <template #icon >
66+ <AccountGroup :size =" 20" />
67+ </template >
68+ {{ t('core', 'People') }}
69+ </NcButton >
70+ </template >
71+ </SearchableList >
72+ <NcButton v-if =" supportFiltering" @click =" closeModal" >
73+ {{ t('core', 'Filter in current view') }}
74+ <template #icon >
75+ <FilterIcon :size =" 20" />
76+ </template >
77+ </NcButton >
78+ </div >
79+ <div class =" unified-search-modal__filters-applied" >
80+ <FilterChip v-for =" filter in filters"
81+ :key =" filter.id"
82+ :text =" filter.name ?? filter.text"
83+ :pretext =" ''"
84+ @delete =" removeFilter(filter)" >
85+ <template #icon >
86+ <NcAvatar v-if =" filter.type === 'person'"
87+ :user =" filter.user"
88+ :size =" 24"
89+ :disable-menu =" true"
90+ :show-user-status =" false"
91+ :hide-favorite =" false" />
92+ <CalendarRangeIcon v-else-if =" filter.type === 'date'" />
93+ <img v-else :src =" filter.icon" alt =" " >
94+ </template >
95+ </FilterChip >
96+ </div >
8797 </div >
8898 <div v-if =" noContentInfo.show" class =" unified-search-modal__no-content" >
8999 <NcEmptyContent :name =" noContentInfo.text" >
92102 </template >
93103 </NcEmptyContent >
94104 </div >
95- <div v-for = " providerResult in results " :key = " providerResult.id " class =" unified-search-modal__results" >
96- <div class = " results" >
105+ <div class =" unified-search-modal__results" >
106+ <div v-for = " providerResult in results" :key = " providerResult.id " class = " result " >
97107 <div class =" result-title" >
98108 <span >{{ providerResult.provider }}</span >
99109 </div >
116126 </div >
117127 </div >
118128 </div >
119- <div v-if =" supportFiltering()" class =" unified-search-modal__results" >
120- <NcButton @click =" closeModal" >
121- {{ t('core', 'Filter in current view') }}
122- <template #icon >
123- <FilterIcon :size =" 20" />
124- </template >
125- </NcButton >
126- </div >
127129 </div >
128130 </NcModal >
129131</template >
@@ -150,6 +152,7 @@ import SearchResult from '../components/UnifiedSearch/SearchResult.vue'
150152
151153import debounce from ' debounce'
152154import { emit } from ' @nextcloud/event-bus'
155+ import { useBrowserLocation } from ' @vueuse/core'
153156import { getProviders , search as unifiedSearch , getContacts } from ' ../services/UnifiedSearchService.js'
154157
155158export default {
@@ -180,6 +183,15 @@ export default {
180183 required: true ,
181184 },
182185 },
186+ setup () {
187+ /**
188+ * Reactive version of window.location
189+ */
190+ const currentLocation = useBrowserLocation ()
191+ return {
192+ currentLocation,
193+ }
194+ },
183195 data () {
184196 return {
185197 providers: [],
@@ -205,22 +217,23 @@ export default {
205217 },
206218
207219 computed: {
208- userContacts: {
209- get () {
210- return this .contacts
211- },
220+ userContacts () {
221+ return this .contacts
212222 },
213- noContentInfo: {
214- get () {
215- const isEmptySearch = this .searchQuery .length === 0
216- const hasNoResults = this .searchQuery .length > 0 && this .results .length === 0
217-
218- return {
219- show: isEmptySearch || hasNoResults,
220- text: this .searching && hasNoResults ? t (' core' , ' Searching …' ) : (isEmptySearch ? t (' core' , ' Start typing in search' ) : t (' core' , ' No matching results' )),
221- icon: MagnifyIcon,
222- }
223- },
223+ noContentInfo () {
224+ const isEmptySearch = this .searchQuery .length === 0
225+ const hasNoResults = this .searchQuery .length > 0 && this .results .length === 0
226+
227+ return {
228+ show: isEmptySearch || hasNoResults,
229+ text: this .searching && hasNoResults ? t (' core' , ' Searching …' ) : (isEmptySearch ? t (' core' , ' Start typing in search' ) : t (' core' , ' No matching results' )),
230+ icon: MagnifyIcon,
231+ }
232+ },
233+ supportFiltering () {
234+ /* Hard coded apps for the moment this would be improved in coming updates. */
235+ const providerPaths = [' /settings/users' , ' /apps/files' , ' /apps/deck' ]
236+ return providerPaths .some ((path ) => this .currentLocation .pathname ? .includes ? .(path))
224237 },
225238 },
226239 watch: {
@@ -522,21 +535,24 @@ export default {
522535 this .internalIsVisible = false
523536 this .searchQuery = ' '
524537 },
525- supportFiltering () {
526- /* Hard coded apps for the moment this would be improved in coming updates. */
527- const providerPaths = [' /settings/users' , ' /apps/files' , ' /apps/deck' ]
528- const currentPath = window .location .pathname .replace (' /index.php' , ' ' )
529- const containsProvider = providerPaths .some (path => currentPath .includes (path))
530- return containsProvider
531- },
532538 },
533539}
534540< / script>
535541
536542< style lang= " scss" scoped>
537543.unified - search- modal {
538- padding : 10px 20px 10px 20px ;
539- height : 60% ;
544+ box- sizing: border- box;
545+ height: 100 % ;
546+
547+ display: flex;
548+ flex- direction: column;
549+
550+ padding- inline: 20px ;
551+ padding- block: 10px ;
552+
553+ & __header {
554+ padding- block- end: 8px ;
555+ }
540556
541557 & __heading {
542558 font- size: 16px ;
@@ -547,14 +563,10 @@ export default {
547563
548564 & __filters {
549565 display: flex;
550- padding-top : 4px ;
566+ flex- wrap: wrap;
567+ gap: 4px ;
551568 justify- content: left;
552-
553- >* {
554- margin-right : 4px ;
555-
556- }
557-
569+ padding- top: 4px ;
558570 }
559571
560572 & __filters- applied {
@@ -570,19 +582,18 @@ export default {
570582 }
571583
572584 & __results {
573- padding : 10 px ;
585+ overflow : hidden scroll ;
574586
575- .results {
576-
577- .result-title {
587+ .result {
588+ & - title {
578589 span {
579590 color: var (-- color- primary- element);
580591 font- weight: bolder;
581592 font- size: 16px ;
582593 }
583594 }
584595
585- .result -footer {
596+ & - footer {
586597 justify- content: space- between;
587598 align- items: center;
588599 display: flex;
@@ -592,20 +603,18 @@ export default {
592603 }
593604}
594605
595- div .v-popper__wrapper {
596- ul {
597- li {
598- ::v- deep button.action- button {
599- align-items : center !important ;
600-
601- img {
606+ .filter - button__icon {
607+ height: 20px ;
602608 width: 20px ;
603- margin : 0 4 px ;
609+ object - fit : contain ;
604610 filter: var (-- background- invert- if - bright);
605- }
611+ padding: 11px ; // align with text to fit at least 44px
612+ }
606613
607- }
608- }
614+ // Ensure modal is accessible on small devices
615+ @media only screen and (max - height : 400px ) {
616+ .unified - search- modal__results {
617+ overflow: unset;
609618 }
610619}
611620< / style>
0 commit comments