Skip to content

Commit

Permalink
Move search result section to component & hide thumbnail if error
Browse files Browse the repository at this point in the history
The section that handles the search result list item is big and complex enough
 to have it's own component. Inside this component, a new method is added to hide
the thumbnail preview if the image load errors out.

Signed-off-by: fenn-cs <fenn25.fn@gmail.com>
Signed-off-by: nextcloud-command <nextcloud-command@users.noreply.github.com>
  • Loading branch information
nfebe authored and nextcloud-command committed Nov 20, 2023
1 parent 8496762 commit 9903f35
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 101 deletions.
162 changes: 162 additions & 0 deletions core/src/components/GlobalSearch/SearchResult.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<template>
<NcListItem class="result-items__item"
:name="title ?? ''"
:bold="false"
@click="openResult(result)">
<template #icon>
<div class="result-items__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),
[icon]: !isValidIconOrPreviewUrl(icon),
}"
:style="{
backgroundImage: isValidIconOrPreviewUrl(icon) ? `url(${icon})` : '',
}">
<img v-if="isValidIconOrPreviewUrl(thumbnailUrl) && !thumbnailHasError"
:src="thumbnailUrl"
@error="thumbnailErrorHandler">
</div>
</template>
<template #subname>
{{ subline }}
</template>
</NcListItem>
</template>

<script>
import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
export default {
name: 'SearchResult',
components: {
NcListItem,
},
props: {
thumbnailUrl: {
type: String,
default: null,
},
title: {
type: String,
required: true,
},
subline: {
type: String,
default: null,
},
resourceUrl: {
type: String,
default: null,
},
icon: {
type: String,
default: '',
},
rounded: {
type: Boolean,
default: false,
},
query: {
type: String,
default: '',
},
/**
* Only used for the first result as a visual feedback
* so we can keep the search input focused but pressing
* enter still opens the first result
*/
focused: {
type: Boolean,
default: false,
},
},
data() {
return {
thumbnailHasError: false,
}
},
methods: {
isValidIconOrPreviewUrl(url) {
return /^https?:\/\//.test(url) || url.startsWith('/')
},
thumbnailErrorHandler() {
this.thumbnailHasError = true
},
},
}
</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);
}
&--no-preview {
background-size: 32px;
}
&--with-thumbnail {
background-size: cover;
}
&--with-thumbnail:not(&--rounded) {
// compensate for border
max-width: $clickable-area - 2px;
max-height: $clickable-area - 2px;
border: 1px solid var(--color-border);
}
img {
// Make sure to keep ratio
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
}
}
}
</style>
101 changes: 3 additions & 98 deletions core/src/views/GlobalSearchModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -97,30 +97,7 @@
<span>{{ providerResult.provider }}</span>
</div>
<ul class="result-items">
<NcListItem v-for="(result, index) in providerResult.results"
:key="index"
class="result-items__item"
:name="result.title ?? ''"
:bold="false"
@click="openResult(result)">
<template #icon>
<div class="result-items__item-icon"
:class="{
'result-items__item-icon--rounded': result.rounded,
'result-items__item-icon--no-preview': !isValidIconOrPreviewUrl(result.thumbnailUrl),
'result-items__item-icon--with-thumbnail': isValidIconOrPreviewUrl(result.thumbnailUrl),
[result.icon]: !isValidIconOrPreviewUrl(result.icon),
}"
:style="{
backgroundImage: isValidIconOrPreviewUrl(result.icon) ? `url(${result.icon})` : '',
}">
<img v-if="isValidIconOrPreviewUrl(result.thumbnailUrl)" :src="result.thumbnailUrl" class="">
</div>
</template>
<template #subname>
{{ result.subline }}
</template>
</NcListItem>
<SearchResult v-for="(result, index) in providerResult.results" :key="index" v-bind="result" />
</ul>
<div class="result-footer">
<NcButton type="tertiary-no-background" @click="loadMoreResultsForProvider(providerResult.id)">
Expand Down Expand Up @@ -158,9 +135,9 @@ 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 NcModal from '@nextcloud/vue/dist/Components/NcModal.js'
import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
import MagnifyIcon from 'vue-material-design-icons/Magnify.vue'
import SearchableList from '../components/GlobalSearch/SearchableList.vue'
import SearchResult from '../components/GlobalSearch/SearchResult.vue'
import debounce from 'debounce'
import { getProviders, search as globalSearch, getContacts } from '../services/GlobalSearchService.js'
Expand All @@ -182,10 +159,10 @@ export default {
NcButton,
NcEmptyContent,
NcModal,
NcListItem,
NcInputField,
MagnifyIcon,
SearchableList,
SearchResult,
},
props: {
isVisible: {
Expand Down Expand Up @@ -512,9 +489,6 @@ export default {
this.dateFilter.text = t('core', `Between ${this.dateFilter.startFrom.toLocaleDateString()} and ${this.dateFilter.endAt.toLocaleDateString()}`)
this.updateDateFilter()
},
isValidIconOrPreviewUrl(url) {
return /^https?:\/\//.test(url) || url.startsWith('/')
},
closeModal() {
this.searchQuery = ''
},
Expand All @@ -523,10 +497,6 @@ export default {
</script>

<style lang="scss" scoped>
@use "sass:math";
$clickable-area: 44px;
$margin: 10px;
.global-search-modal {
padding: 10px 20px 10px 20px;
height: 60%;
Expand Down Expand Up @@ -574,71 +544,6 @@ $margin: 10px;
}
}
.result-items {
::v-deep &__item {
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);
}
&--no-preview {
background-size: 32px;
}
&--with-thumbnail {
background-size: cover;
}
&--with-thumbnail:not(&--rounded) {
// compensate for border
max-width: $clickable-area - 2px;
max-height: $clickable-area - 2px;
border: 1px solid var(--color-border);
}
img {
// Make sure to keep ratio
width: 100%;
height: 100%;
object-fit: cover;
object-position: center;
}
}
}
}
.result-footer {
justify-content: space-between;
align-items: center;
Expand Down
4 changes: 2 additions & 2 deletions dist/core-global-search.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/core-global-search.js.map

Large diffs are not rendered by default.

0 comments on commit 9903f35

Please sign in to comment.