Skip to content

Commit

Permalink
enh(UnifiedSearch): Keep the searchbar on top of the modal
Browse files Browse the repository at this point in the history
Signed-off-by: Ferdinand Thiessen <[email protected]>
  • Loading branch information
susnux committed Dec 12, 2023
1 parent c734a18 commit b78a856
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 230 deletions.
259 changes: 134 additions & 125 deletions core/src/views/UnifiedSearchModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,78 +12,88 @@
@update:is-open="showDateRangeModal = $event" />
<!-- Unified search form -->
<div ref="unifiedSearch" class="unified-search-modal">
<h1>{{ t('core', 'Unified search') }}</h1>
<NcInputField ref="searchInput"
:value.sync="searchQuery"
type="text"
:label="t('core', 'Search apps, files, tags, messages') + '...'"
@update:value="debouncedFind" />
<div class="unified-search-modal__filters">
<NcActions :menu-name="t('core', 'Apps and Settings')" :open.sync="providerActionMenuIsOpen">
<template #icon>
<ListBox :size="20" />
</template>
<NcActionButton v-for="provider in providers" :key="provider.id" @click="addProviderFilter(provider)">
<div class="unified-search-modal__header">
<h1>{{ t('core', 'Unified search') }}</h1>
<NcInputField ref="searchInput"
:value.sync="searchQuery"
type="text"
:label="t('core', 'Search apps, files, tags, messages') + '...'"
@update:value="debouncedFind" />
<div class="unified-search-modal__filters">
<NcActions :menu-name="t('core', 'Apps and Settings')" :open.sync="providerActionMenuIsOpen">
<template #icon>
<img :src="provider.icon">
<ListBox :size="20" />
</template>
{{ t('core', provider.name) }}
</NcActionButton>
</NcActions>
<NcActions :menu-name="t('core', 'Date')" :open.sync="dateActionMenuIsOpen">
<template #icon>
<CalendarRangeIcon :size="20" />
</template>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('today')">
{{ t('core', 'Today') }}
</NcActionButton>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('7days')">
{{ t('core', 'Last 7 days') }}
</NcActionButton>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('30days')">
{{ t('core', 'Last 30 days') }}
</NcActionButton>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('thisyear')">
{{ t('core', 'This year') }}
</NcActionButton>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('lastyear')">
{{ t('core', 'Last year') }}
</NcActionButton>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('custom')">
{{ t('core', 'Custom date range') }}
</NcActionButton>
</NcActions>
<SearchableList :label-text="t('core', 'Search people')"
:search-list="userContacts"
:empty-content-text="t('core', 'Not found')"
@item-selected="applyPersonFilter">
<template #trigger>
<NcButton>
<NcActionButton v-for="provider in providers"
:key="provider.id"
@click="addProviderFilter(provider)">
<template #icon>
<AccountGroup :size="20" />
<img :src="provider.icon" class="filter-button__icon" alt="">
</template>
{{ t('core', 'People') }}
</NcButton>
</template>
</SearchableList>
</div>
<div class="unified-search-modal__filters-applied">
<FilterChip v-for="filter in filters"
:key="filter.id"
:text="filter.name ?? filter.text"
:pretext="''"
@delete="removeFilter(filter)">
<template #icon>
<NcAvatar v-if="filter.type === 'person'"
:user="filter.user"
:size="24"
:disable-menu="true"
:show-user-status="false"
:hide-favorite="false" />
<CalendarRangeIcon v-else-if="filter.type === 'date'" />
<img v-else :src="filter.icon" alt="">
</template>
</FilterChip>
{{ provider.name }}
</NcActionButton>
</NcActions>
<NcActions :menu-name="t('core', 'Date')" :open.sync="dateActionMenuIsOpen">
<template #icon>
<CalendarRangeIcon :size="20" />
</template>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('today')">
{{ t('core', 'Today') }}
</NcActionButton>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('7days')">
{{ t('core', 'Last 7 days') }}
</NcActionButton>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('30days')">
{{ t('core', 'Last 30 days') }}
</NcActionButton>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('thisyear')">
{{ t('core', 'This year') }}
</NcActionButton>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('lastyear')">
{{ t('core', 'Last year') }}
</NcActionButton>
<NcActionButton :close-after-click="true" @click="applyQuickDateRange('custom')">
{{ t('core', 'Custom date range') }}
</NcActionButton>
</NcActions>
<SearchableList :label-text="t('core', 'Search people')"
:search-list="userContacts"
:empty-content-text="t('core', 'Not found')"
@item-selected="applyPersonFilter">
<template #trigger>
<NcButton>
<template #icon>
<AccountGroup :size="20" />
</template>
{{ t('core', 'People') }}
</NcButton>
</template>
</SearchableList>
<NcButton v-if="supportFiltering" @click="closeModal">
{{ t('core', 'Filter in current view') }}
<template #icon>
<FilterIcon :size="20" />
</template>
</NcButton>
</div>
<div class="unified-search-modal__filters-applied">
<FilterChip v-for="filter in filters"
:key="filter.id"
:text="filter.name ?? filter.text"
:pretext="''"
@delete="removeFilter(filter)">
<template #icon>
<NcAvatar v-if="filter.type === 'person'"
:user="filter.user"
:size="24"
:disable-menu="true"
:show-user-status="false"
:hide-favorite="false" />
<CalendarRangeIcon v-else-if="filter.type === 'date'" />
<img v-else :src="filter.icon" alt="">
</template>
</FilterChip>
</div>
</div>
<div v-if="noContentInfo.show" class="unified-search-modal__no-content">
<NcEmptyContent :name="noContentInfo.text">
Expand All @@ -92,8 +102,8 @@
</template>
</NcEmptyContent>
</div>
<div v-for="providerResult in results" :key="providerResult.id" class="unified-search-modal__results">
<div class="results">
<div class="unified-search-modal__results">
<div v-for="providerResult in results" :key="providerResult.id" class="result">
<div class="result-title">
<span>{{ providerResult.provider }}</span>
</div>
Expand All @@ -116,14 +126,6 @@
</div>
</div>
</div>
<div v-if="supportFiltering()" class="unified-search-modal__results">
<NcButton @click="closeModal">
{{ t('core', 'Filter in current view') }}
<template #icon>
<FilterIcon :size="20" />
</template>
</NcButton>
</div>
</div>
</NcModal>
</template>
Expand All @@ -150,6 +152,7 @@ import SearchResult from '../components/UnifiedSearch/SearchResult.vue'
import debounce from 'debounce'
import { emit } from '@nextcloud/event-bus'
import { useBrowserLocation } from '@vueuse/core'
import { getProviders, search as unifiedSearch, getContacts } from '../services/UnifiedSearchService.js'
export default {
Expand Down Expand Up @@ -180,6 +183,15 @@ export default {
required: true,
},
},
setup() {
/**
* Reactive version of window.location
*/
const currentLocation = useBrowserLocation()
return {
currentLocation,
}
},
data() {
return {
providers: [],
Expand All @@ -205,22 +217,23 @@ export default {
},
computed: {
userContacts: {
get() {
return this.contacts
},
userContacts() {
return this.contacts
},
noContentInfo: {
get() {
const isEmptySearch = this.searchQuery.length === 0
const hasNoResults = this.searchQuery.length > 0 && this.results.length === 0
return {
show: isEmptySearch || hasNoResults,
text: this.searching && hasNoResults ? t('core', 'Searching …') : (isEmptySearch ? t('core', 'Start typing in search') : t('core', 'No matching results')),
icon: MagnifyIcon,
}
},
noContentInfo() {
const isEmptySearch = this.searchQuery.length === 0
const hasNoResults = this.searchQuery.length > 0 && this.results.length === 0
return {
show: isEmptySearch || hasNoResults,
text: this.searching && hasNoResults ? t('core', 'Searching …') : (isEmptySearch ? t('core', 'Start typing in search') : t('core', 'No matching results')),
icon: MagnifyIcon,
}
},
supportFiltering() {
/* Hard coded apps for the moment this would be improved in coming updates. */
const providerPaths = ['/settings/users', '/apps/files', '/apps/deck']
return providerPaths.some((path) => this.currentLocation.pathname?.includes?.(path))
},
},
watch: {
Expand Down Expand Up @@ -522,21 +535,24 @@ export default {
this.internalIsVisible = false
this.searchQuery = ''
},
supportFiltering() {
/* Hard coded apps for the moment this would be improved in coming updates. */
const providerPaths = ['/settings/users', '/apps/files', '/apps/deck']
const currentPath = window.location.pathname.replace('/index.php', '')
const containsProvider = providerPaths.some(path => currentPath.includes(path))
return containsProvider
},
},
}
</script>
<style lang="scss" scoped>
.unified-search-modal {
padding: 10px 20px 10px 20px;
height: 60%;
box-sizing: border-box;
height: 100%;
display: flex;
flex-direction: column;
padding-inline: 20px;
padding-block: 10px;
&__header {
padding-block-end: 8px;
}
&__heading {
font-size: 16px;
Expand All @@ -547,14 +563,10 @@ export default {
&__filters {
display: flex;
padding-top: 4px;
flex-wrap: wrap;
gap: 4px;
justify-content: left;
>* {
margin-right: 4px;
}
padding-top: 4px;
}
&__filters-applied {
Expand All @@ -570,19 +582,18 @@ export default {
}
&__results {
padding: 10px;
overflow: hidden scroll;
.results {
.result-title {
.result {
&-title {
span {
color: var(--color-primary-element);
font-weight: bolder;
font-size: 16px;
}
}
.result-footer {
&-footer {
justify-content: space-between;
align-items: center;
display: flex;
Expand All @@ -592,20 +603,18 @@ export default {
}
}
div.v-popper__wrapper {
ul {
li {
::v-deep button.action-button {
align-items: center !important;
img {
.filter-button__icon {
height: 20px;
width: 20px;
margin: 0 4px;
object-fit: contain;
filter: var(--background-invert-if-bright);
}
padding: 11px; // align with text to fit at least 44px
}
}
}
// Ensure modal is accessible on small devices
@media only screen and (max-height: 400px) {
.unified-search-modal__results {
overflow: unset;
}
}
</style>
Loading

0 comments on commit b78a856

Please sign in to comment.