feat: settings background script setup (#2104)
Co-authored-by: Dominik Pschenitschni <mail@celement.de> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/2104 Reviewed-by: konrad <k@knt.li> Co-authored-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de> Co-committed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
This commit is contained in:
parent
a8d4892a0f
commit
ff655808b3
8 changed files with 275 additions and 244 deletions
|
@ -59,7 +59,7 @@ describe('Lists', () => {
|
|||
.click()
|
||||
cy.get('#title')
|
||||
.type(`{selectall}${newListName}`)
|
||||
cy.get('footer.modal-card-foot .button')
|
||||
cy.get('footer.card-footer .button')
|
||||
.contains('Save')
|
||||
.click()
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ describe('Namepaces', () => {
|
|||
.should('equal', newNamespaces[0].title) // wait until the namespace data is loaded
|
||||
cy.get('#namespacetext')
|
||||
.type(`{selectall}${newNamespaceName}`)
|
||||
cy.get('footer.modal-card-foot .button')
|
||||
cy.get('footer.card-footer .button')
|
||||
.contains('Save')
|
||||
.click()
|
||||
|
||||
|
|
|
@ -69,9 +69,11 @@ const showIconOnly = computed(() => props.icon !== '' && typeof slots.default ==
|
|||
text-transform: uppercase;
|
||||
font-size: 0.85rem;
|
||||
font-weight: bold;
|
||||
height: auto;
|
||||
min-height: $button-height;
|
||||
box-shadow: var(--shadow-sm);
|
||||
display: inline-flex;
|
||||
white-space: break-spaces;
|
||||
|
||||
&:hover {
|
||||
box-shadow: var(--shadow-md);
|
||||
|
|
|
@ -16,11 +16,21 @@
|
|||
</span>
|
||||
</BaseButton>
|
||||
</header>
|
||||
<div class="card-content loader-container" :class="{'p-0': !padding, 'is-loading': loading}">
|
||||
<div
|
||||
class="card-content loader-container"
|
||||
:class="{
|
||||
'p-0': !padding,
|
||||
'is-loading': loading
|
||||
}"
|
||||
>
|
||||
<div :class="{'content': hasContent}">
|
||||
<slot></slot>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer v-if="$slots.footer" class="card-footer">
|
||||
<slot name="footer" />
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -76,9 +86,11 @@ defineEmits(['close'])
|
|||
border-radius: $radius $radius 0 0;
|
||||
}
|
||||
|
||||
// FIXME: should maybe be merged somehow with modal
|
||||
:deep(.modal-card-foot) {
|
||||
.card-footer {
|
||||
background-color: var(--grey-50);
|
||||
border-top: 0;
|
||||
padding: var(--modal-card-head-padding);
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
</style>
|
|
@ -4,38 +4,41 @@
|
|||
:title="title"
|
||||
:shadow="false"
|
||||
:padding="false"
|
||||
class="has-text-left has-overflow"
|
||||
class="has-text-left"
|
||||
:has-close="true"
|
||||
@close="$router.back()"
|
||||
:loading="loading"
|
||||
>
|
||||
<div class="p-4">
|
||||
<slot></slot>
|
||||
<slot />
|
||||
</div>
|
||||
<footer class="modal-card-foot is-flex is-justify-content-flex-end">
|
||||
<x-button
|
||||
v-if="tertiary !== ''"
|
||||
:shadow="false"
|
||||
variant="tertiary"
|
||||
@click.prevent.stop="$emit('tertiary')"
|
||||
>
|
||||
{{ tertiary }}
|
||||
</x-button>
|
||||
<x-button
|
||||
variant="secondary"
|
||||
@click.prevent.stop="$router.back()"
|
||||
>
|
||||
{{ $t('misc.cancel') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
variant="primary"
|
||||
@click.prevent.stop="primary()"
|
||||
:icon="primaryIcon"
|
||||
:disabled="primaryDisabled"
|
||||
>
|
||||
{{ primaryLabel || $t('misc.create') }}
|
||||
</x-button>
|
||||
</footer>
|
||||
|
||||
<template #footer>
|
||||
<slot name="footer">
|
||||
<x-button
|
||||
v-if="tertiary !== ''"
|
||||
:shadow="false"
|
||||
variant="tertiary"
|
||||
@click.prevent.stop="$emit('tertiary')"
|
||||
>
|
||||
{{ tertiary }}
|
||||
</x-button>
|
||||
<x-button
|
||||
variant="secondary"
|
||||
@click.prevent.stop="$router.back()"
|
||||
>
|
||||
{{ $t('misc.cancel') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
variant="primary"
|
||||
@click.prevent.stop="primary()"
|
||||
:icon="primaryIcon"
|
||||
:disabled="primaryDisabled || loading"
|
||||
>
|
||||
{{ primaryLabel || $t('misc.create') }}
|
||||
</x-button>
|
||||
</slot>
|
||||
</template>
|
||||
</card>
|
||||
</modal>
|
||||
</template>
|
||||
|
|
|
@ -19,17 +19,16 @@
|
|||
{{ $t('about.apiVersion', {version: apiVersion}) }}
|
||||
</p>
|
||||
</div>
|
||||
<footer class="modal-card-foot is-flex is-justify-content-flex-end">
|
||||
<template #footer>
|
||||
<x-button
|
||||
variant="secondary"
|
||||
@click.prevent.stop="$router.back()"
|
||||
>
|
||||
{{ $t('misc.close') }}
|
||||
</x-button>
|
||||
</footer>
|
||||
</template>
|
||||
</card>
|
||||
</modal>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
|
|
@ -47,14 +47,17 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<x-button
|
||||
:loading="savedFilterService.loading"
|
||||
:disabled="savedFilterService.loading"
|
||||
@click="create()"
|
||||
class="is-fullwidth"
|
||||
>
|
||||
{{ $t('filters.create.action') }}
|
||||
</x-button>
|
||||
|
||||
<template #footer>
|
||||
<x-button
|
||||
:loading="savedFilterService.loading"
|
||||
:disabled="savedFilterService.loading"
|
||||
@click="create()"
|
||||
class="is-fullwidth"
|
||||
>
|
||||
{{ $t('filters.create.action') }}
|
||||
</x-button>
|
||||
</template>
|
||||
</card>
|
||||
</modal>
|
||||
</template>
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
<template>
|
||||
<create-edit
|
||||
v-if="uploadBackgroundEnabled || unsplashBackgroundEnabled"
|
||||
:title="$t('list.background.title')"
|
||||
primary-label=""
|
||||
:loading="backgroundService.loading"
|
||||
class="list-background-setting"
|
||||
:wide="true"
|
||||
v-if="uploadBackgroundEnabled || unsplashBackgroundEnabled"
|
||||
:tertiary="hasBackground ? $t('list.background.remove') : ''"
|
||||
@tertiary="removeBackground()"
|
||||
>
|
||||
<div class="mb-4" v-if="uploadBackgroundEnabled">
|
||||
<input
|
||||
|
@ -19,7 +16,7 @@
|
|||
/>
|
||||
<x-button
|
||||
:loading="backgroundUploadService.loading"
|
||||
@click="$refs.backgroundUploadInput.click()"
|
||||
@click="backgroundUploadInput?.click()"
|
||||
variant="primary"
|
||||
>
|
||||
{{ $t('list.background.upload') }}
|
||||
|
@ -28,245 +25,260 @@
|
|||
<template v-if="unsplashBackgroundEnabled">
|
||||
<input
|
||||
:class="{'is-loading': backgroundService.loading}"
|
||||
@keyup="() => debounceNewBackgroundSearch()"
|
||||
@keyup="debounceNewBackgroundSearch()"
|
||||
class="input is-expanded"
|
||||
:placeholder="$t('list.background.searchPlaceholder')"
|
||||
type="text"
|
||||
v-model="backgroundSearchTerm"
|
||||
/>
|
||||
<p class="unsplash-link">
|
||||
<BaseButton href="https://unsplash.com">{{ $t('list.background.poweredByUnsplash') }}</BaseButton>
|
||||
|
||||
<p class="unsplash-credit">
|
||||
<BaseButton class="unsplash-credit__link" href="https://unsplash.com">{{ $t('list.background.poweredByUnsplash') }}</BaseButton>
|
||||
</p>
|
||||
<div class="image-search-result">
|
||||
<a
|
||||
|
||||
<ul class="image-search__result-list">
|
||||
<li
|
||||
v-for="im in backgroundSearchResult"
|
||||
class="image-search__result-item"
|
||||
:key="im.id"
|
||||
:style="{'background-image': `url(${backgroundBlurHashes[im.id]})`}"
|
||||
@click="() => setBackground(im.id)"
|
||||
class="image"
|
||||
v-for="im in backgroundSearchResult">
|
||||
>
|
||||
<transition name="fade">
|
||||
<img :src="backgroundThumbs[im.id]" alt="" v-if="backgroundThumbs[im.id]"/>
|
||||
<BaseButton
|
||||
v-if="backgroundThumbs[im.id]"
|
||||
class="image-search__image-button"
|
||||
@click="setBackground(im.id)"
|
||||
>
|
||||
<img class="image-search__image" :src="backgroundThumbs[im.id]" alt="" />
|
||||
</BaseButton>
|
||||
</transition>
|
||||
<a
|
||||
|
||||
<BaseButton
|
||||
:href="`https://unsplash.com/@${im.info.author}`"
|
||||
rel="noreferrer noopener nofollow"
|
||||
target="_blank"
|
||||
class="info">
|
||||
class="image-search__info"
|
||||
>
|
||||
{{ im.info.authorName }}
|
||||
</a>
|
||||
</a>
|
||||
</div>
|
||||
</BaseButton>
|
||||
</li>
|
||||
</ul>
|
||||
<x-button
|
||||
v-if="backgroundSearchResult.length > 0"
|
||||
:disabled="backgroundService.loading"
|
||||
@click="() => searchBackgrounds(currentPage + 1)"
|
||||
@click="searchBackgrounds(currentPage + 1)"
|
||||
class="is-load-more-button mt-4"
|
||||
:shadow="false"
|
||||
variant="secondary"
|
||||
v-if="backgroundSearchResult.length > 0"
|
||||
>
|
||||
{{ backgroundService.loading ? $t('misc.loading') : $t('list.background.loadMore') }}
|
||||
</x-button>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<x-button
|
||||
v-if="hasBackground"
|
||||
:shadow="false"
|
||||
variant="tertiary"
|
||||
class="is-danger"
|
||||
@click.prevent.stop="removeBackground"
|
||||
>
|
||||
{{ $t('list.background.remove') }}
|
||||
</x-button>
|
||||
<x-button
|
||||
variant="secondary"
|
||||
@click.prevent.stop="$router.back()"
|
||||
>
|
||||
{{ $t('misc.close') }}
|
||||
</x-button>
|
||||
</template>
|
||||
</create-edit>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent} from 'vue'
|
||||
import {mapState} from 'vuex'
|
||||
import {getBlobFromBlurHash} from '../../../helpers/getBlobFromBlurHash'
|
||||
export default defineComponent({ name: 'list-setting-background' })
|
||||
</script>
|
||||
|
||||
import BackgroundUnsplashService from '../../../services/backgroundUnsplash'
|
||||
import BackgroundUploadService from '../../../services/backgroundUpload'
|
||||
import ListService from '@/services/list'
|
||||
import {CURRENT_LIST} from '@/store/mutation-types'
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
<script setup lang="ts">
|
||||
import {ref, computed, shallowReactive} from 'vue'
|
||||
import {useI18n} from 'vue-i18n'
|
||||
import {useStore} from 'vuex'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import debounce from 'lodash.debounce'
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
|
||||
import BackgroundUnsplashService from '@/services/backgroundUnsplash'
|
||||
import BackgroundUploadService from '@/services/backgroundUpload'
|
||||
import ListService from '@/services/list'
|
||||
import BackgroundImageModel from '@/models/backgroundImage'
|
||||
|
||||
import {getBlobFromBlurHash} from '@/helpers/getBlobFromBlurHash'
|
||||
import {useTitle} from '@/composables/useTitle'
|
||||
import {CURRENT_LIST} from '@/store/mutation-types'
|
||||
|
||||
import CreateEdit from '@/components/misc/create-edit.vue'
|
||||
import { success } from '@/message'
|
||||
|
||||
const SEARCH_DEBOUNCE = 300
|
||||
|
||||
export default defineComponent({
|
||||
name: 'list-setting-background',
|
||||
components: {CreateEdit, BaseButton},
|
||||
data() {
|
||||
return {
|
||||
backgroundService: new BackgroundUnsplashService(),
|
||||
backgroundSearchTerm: '',
|
||||
backgroundSearchResult: [],
|
||||
backgroundThumbs: {},
|
||||
backgroundBlurHashes: {},
|
||||
currentPage: 1,
|
||||
const {t} = useI18n()
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// We're using debounce to not search on every keypress but with a delay.
|
||||
debounceNewBackgroundSearch: debounce(this.newBackgroundSearch, SEARCH_DEBOUNCE, {
|
||||
trailing: true,
|
||||
}),
|
||||
useTitle(() => t('list.background.title'))
|
||||
|
||||
backgroundUploadService: new BackgroundUploadService(),
|
||||
listService: new ListService(),
|
||||
}
|
||||
},
|
||||
computed: mapState({
|
||||
unsplashBackgroundEnabled: state => state.config.enabledBackgroundProviders.includes('unsplash'),
|
||||
uploadBackgroundEnabled: state => state.config.enabledBackgroundProviders.includes('upload'),
|
||||
currentList: state => state.currentList,
|
||||
hasBackground: state => state.background !== null,
|
||||
}),
|
||||
created() {
|
||||
this.setTitle(this.$t('list.background.title'))
|
||||
// Show the default collection of backgrounds
|
||||
this.newBackgroundSearch()
|
||||
},
|
||||
methods: {
|
||||
newBackgroundSearch() {
|
||||
if (!this.unsplashBackgroundEnabled) {
|
||||
return
|
||||
}
|
||||
// This is an extra method to reset a few things when searching to not break loading more photos.
|
||||
this.backgroundSearchResult = []
|
||||
this.backgroundThumbs = {}
|
||||
this.searchBackgrounds()
|
||||
},
|
||||
const backgroundService = shallowReactive(new BackgroundUnsplashService())
|
||||
const backgroundSearchTerm = ref('')
|
||||
const backgroundSearchResult = ref([])
|
||||
const backgroundThumbs = ref<Record<string, string>>({})
|
||||
const backgroundBlurHashes = ref<Record<string, string>>({})
|
||||
const currentPage = ref(1)
|
||||
|
||||
async searchBackgrounds(page = 1) {
|
||||
this.currentPage = page
|
||||
const result = await this.backgroundService.getAll({}, {s: this.backgroundSearchTerm, p: page})
|
||||
this.backgroundSearchResult = this.backgroundSearchResult.concat(result)
|
||||
result.forEach(background => {
|
||||
getBlobFromBlurHash(background.blurHash)
|
||||
.then(b => {
|
||||
this.backgroundBlurHashes[background.id] = window.URL.createObjectURL(b)
|
||||
})
|
||||
|
||||
this.backgroundService.thumb(background)
|
||||
.then(b => {
|
||||
this.backgroundThumbs[background.id] = b
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
async setBackground(backgroundId) {
|
||||
// Don't set a background if we're in the process of setting one
|
||||
if (this.backgroundService.loading) {
|
||||
return
|
||||
}
|
||||
|
||||
const list = await this.backgroundService.update({id: backgroundId, listId: this.$route.params.listId})
|
||||
await this.$store.dispatch(CURRENT_LIST, {list, forceUpdate: true})
|
||||
this.$store.commit('namespaces/setListInNamespaceById', list)
|
||||
this.$store.commit('lists/setList', list)
|
||||
this.$message.success({message: this.$t('list.background.success')})
|
||||
},
|
||||
|
||||
async uploadBackground() {
|
||||
if (this.$refs.backgroundUploadInput.files.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const list = await this.backgroundUploadService.create(this.$route.params.listId, this.$refs.backgroundUploadInput.files[0])
|
||||
await this.$store.dispatch(CURRENT_LIST, {list, forceUpdate: true})
|
||||
this.$store.commit('namespaces/setListInNamespaceById', list)
|
||||
this.$store.commit('lists/setList', list)
|
||||
this.$message.success({message: this.$t('list.background.success')})
|
||||
},
|
||||
|
||||
async removeBackground() {
|
||||
const list = await this.listService.removeBackground(this.currentList)
|
||||
await this.$store.dispatch(CURRENT_LIST, {list, forceUpdate: true})
|
||||
this.$store.commit('namespaces/setListInNamespaceById', list)
|
||||
this.$store.commit('lists/setList', list)
|
||||
this.$message.success({message: this.$t('list.background.removeSuccess')})
|
||||
this.$router.back()
|
||||
},
|
||||
},
|
||||
// We're using debounce to not search on every keypress but with a delay.
|
||||
const debounceNewBackgroundSearch = debounce(newBackgroundSearch, SEARCH_DEBOUNCE, {
|
||||
trailing: true,
|
||||
})
|
||||
|
||||
const backgroundUploadService = ref(new BackgroundUploadService())
|
||||
const listService = ref(new ListService())
|
||||
|
||||
const unsplashBackgroundEnabled = computed(() => store.state.config.enabledBackgroundProviders.includes('unsplash'))
|
||||
const uploadBackgroundEnabled = computed(() => store.state.config.enabledBackgroundProviders.includes('upload'))
|
||||
const currentList = computed(() => store.state.currentList)
|
||||
const hasBackground = computed(() => store.state.background !== null)
|
||||
|
||||
// Show the default collection of backgrounds
|
||||
newBackgroundSearch()
|
||||
|
||||
function newBackgroundSearch() {
|
||||
if (!unsplashBackgroundEnabled.value) {
|
||||
return
|
||||
}
|
||||
// This is an extra method to reset a few things when searching to not break loading more photos.
|
||||
backgroundSearchResult.value = []
|
||||
backgroundThumbs.value = {}
|
||||
searchBackgrounds()
|
||||
}
|
||||
|
||||
async function searchBackgrounds(page = 1) {
|
||||
currentPage.value = page
|
||||
const result = await backgroundService.getAll({}, {s: backgroundSearchTerm.value, p: page})
|
||||
backgroundSearchResult.value = backgroundSearchResult.value.concat(result)
|
||||
result.forEach((background: BackgroundImageModel) => {
|
||||
getBlobFromBlurHash(background.blurHash)
|
||||
.then((b) => {
|
||||
backgroundBlurHashes.value[background.id] = window.URL.createObjectURL(b)
|
||||
})
|
||||
|
||||
backgroundService.thumb(background).then(b => {
|
||||
backgroundThumbs.value[background.id] = b
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function setBackground(backgroundId: string) {
|
||||
// Don't set a background if we're in the process of setting one
|
||||
if (backgroundService.loading) {
|
||||
return
|
||||
}
|
||||
|
||||
const list = await backgroundService.update({id: backgroundId, listId: route.params.listId})
|
||||
await store.dispatch(CURRENT_LIST, {list, forceUpdate: true})
|
||||
store.commit('namespaces/setListInNamespaceById', list)
|
||||
store.commit('lists/setList', list)
|
||||
success({message: t('list.background.success')})
|
||||
}
|
||||
|
||||
const backgroundUploadInput = ref<HTMLInputElement | null>(null)
|
||||
async function uploadBackground() {
|
||||
if (backgroundUploadInput.value?.files?.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const list = await backgroundUploadService.value.create(route.params.listId, backgroundUploadInput.value?.files[0])
|
||||
await store.dispatch(CURRENT_LIST, {list, forceUpdate: true})
|
||||
store.commit('namespaces/setListInNamespaceById', list)
|
||||
store.commit('lists/setList', list)
|
||||
success({message: t('list.background.success')})
|
||||
}
|
||||
|
||||
async function removeBackground() {
|
||||
const list = await listService.value.removeBackground(currentList.value)
|
||||
await store.dispatch(CURRENT_LIST, {list, forceUpdate: true})
|
||||
store.commit('namespaces/setListInNamespaceById', list)
|
||||
store.commit('lists/setList', list)
|
||||
success({message: t('list.background.removeSuccess')})
|
||||
router.back()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.list-background-setting {
|
||||
.unsplash-credit {
|
||||
text-align: right;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
.unsplash-link {
|
||||
text-align: right;
|
||||
font-size: .8rem;
|
||||
.unsplash-credit__link {
|
||||
color: var(--grey-800);
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--grey-800);
|
||||
}
|
||||
.image-search__result-list {
|
||||
--items-per-row: 1;
|
||||
margin: 1rem 0 0;
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
grid-template-columns: repeat(var(--items-per-row), 1fr);
|
||||
|
||||
@media screen and (min-width: $mobile) {
|
||||
--items-per-row: 2;
|
||||
}
|
||||
|
||||
.image-search-result {
|
||||
margin-top: 1rem;
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
|
||||
.image {
|
||||
width: calc(100% / 5 - 1rem);
|
||||
height: 120px;
|
||||
margin: .5rem;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
@media screen and (min-width: $desktop) {
|
||||
&:nth-child(5n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $desktop) {
|
||||
width: calc(100% / 4 - 1rem);
|
||||
|
||||
&:nth-child(4n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $tablet) {
|
||||
width: calc(100% / 2 - 1rem);
|
||||
|
||||
&:nth-child(2n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: ($mobile)) {
|
||||
width: calc(100% - 1rem);
|
||||
|
||||
&:nth-child(1n) {
|
||||
break-after: always;
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
align-self: flex-end;
|
||||
display: block;
|
||||
opacity: 0;
|
||||
width: 100%;
|
||||
padding: .25rem 0;
|
||||
text-align: center;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
font-size: .75rem;
|
||||
font-weight: bold;
|
||||
color: var(--white);
|
||||
transition: opacity $transition;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&:hover .info {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@media screen and (min-width: $tablet) {
|
||||
--items-per-row: 4;
|
||||
}
|
||||
|
||||
.is-load-more-button {
|
||||
margin: 1rem auto 0 !important;
|
||||
display: block;
|
||||
width: 200px;
|
||||
@media screen and (min-width: $tablet) {
|
||||
--items-per-row: 5;
|
||||
}
|
||||
}
|
||||
|
||||
.image-search__result-item {
|
||||
margin-top: 0; // FIXME: removes padding from .content
|
||||
aspect-ratio: 16 / 10;
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.image-search__image-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.image-search__image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.image-search__info {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: .25rem 0;
|
||||
opacity: 0;
|
||||
text-align: center;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
font-size: .75rem;
|
||||
font-weight: bold;
|
||||
color: var(--white);
|
||||
transition: opacity $transition;
|
||||
}
|
||||
.image-search__result-item:hover .image-search__info {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.is-load-more-button {
|
||||
margin: 1rem auto 0 !important;
|
||||
display: block;
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
Loading…
Reference in a new issue