stash
This commit is contained in:
parent
109f7170c7
commit
cb0437c98b
6 changed files with 103 additions and 111 deletions
|
|
@ -56,4 +56,15 @@
|
|||
padding-left: 1.25rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.img-preview-fixed {
|
||||
height: 160px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.img-preview-fluid {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
}
|
||||
35
frontend/src/scss/_masonry.scss
Normal file
35
frontend/src/scss/_masonry.scss
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Shared by any view rendering a Pinterest-style image grid (Inventory.vue,
|
||||
// Search.vue, Files.vue) - keeps their column counts and breakpoints in sync.
|
||||
.masonry-grid {
|
||||
column-count: 2;
|
||||
column-gap: 1rem;
|
||||
}
|
||||
|
||||
.masonry-item {
|
||||
break-inside: avoid;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
.masonry-grid {
|
||||
column-count: 3;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
.masonry-grid {
|
||||
column-count: 4;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl) {
|
||||
.masonry-grid {
|
||||
column-count: 6;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xxl) {
|
||||
.masonry-grid {
|
||||
column-count: 12;
|
||||
}
|
||||
}
|
||||
|
|
@ -95,6 +95,7 @@ $body-color: $gray-700;
|
|||
@import "tags";
|
||||
@import "dropdown";
|
||||
@import "label-fonts";
|
||||
@import "masonry";
|
||||
|
||||
#root, body, html {
|
||||
height: 100%;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<div class="masonry-item" v-for="file in only_images(files)" :key="file.id">
|
||||
<deletable-wrapper confirm-message="Delete this file? This cannot be undone." @delete="deleteFile(file)">
|
||||
<authenticated-image :src="thumbnailPathForHash(file.hash)" :owner="file.owner"
|
||||
class="img-preview-masonry"/>
|
||||
class="img-preview-fluid"/>
|
||||
</deletable-wrapper>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -72,51 +72,9 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../scss/breakpoints";
|
||||
|
||||
.img-preview-masonry {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
<style scoped>
|
||||
.masonry-item :deep(span) {
|
||||
display: block !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.masonry-grid {
|
||||
column-count: 2;
|
||||
column-gap: 1rem;
|
||||
}
|
||||
|
||||
.masonry-item {
|
||||
break-inside: avoid;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
.masonry-grid {
|
||||
column-count: 3;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
.masonry-grid {
|
||||
column-count: 4;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl) {
|
||||
.masonry-grid {
|
||||
column-count: 6;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xxl) {
|
||||
.masonry-grid {
|
||||
column-count: 12;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -83,7 +83,7 @@
|
|||
<authenticated-image v-if="only_images(item.files).length > 0"
|
||||
:src="only_images(item.files)[0].name"
|
||||
:owner="only_images(item.files)[0].owner"
|
||||
class="card-img-top img-preview-masonry"/>
|
||||
class="card-img-top img-preview-fluid"/>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-0">
|
||||
<router-link :to="itemRoute(item)">
|
||||
|
|
@ -127,7 +127,7 @@
|
|||
<authenticated-image v-if="only_images(item.files).length > 0"
|
||||
:src="only_images(item.files)[0].name"
|
||||
:owner="only_images(item.files)[0].owner"
|
||||
class="card-img-top img-preview-grid"/>
|
||||
class="card-img-top img-preview-fixed"/>
|
||||
<div class="card-body d-flex flex-column flex-grow-1">
|
||||
<h5 class="card-title mb-0">
|
||||
<router-link :to="itemRoute(item)">
|
||||
|
|
@ -299,58 +299,11 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "../scss/breakpoints";
|
||||
|
||||
.img-preview-grid {
|
||||
height: 160px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.img-preview-masonry {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
<style scoped>
|
||||
.table-action {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.masonry-grid {
|
||||
column-count: 2;
|
||||
column-gap: 1rem;
|
||||
}
|
||||
|
||||
.masonry-item {
|
||||
break-inside: avoid;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
.masonry-grid {
|
||||
column-count: 3;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(lg) {
|
||||
.masonry-grid {
|
||||
column-count: 4;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xl) {
|
||||
.masonry-grid {
|
||||
column-count: 6;
|
||||
}
|
||||
}
|
||||
|
||||
@include media-breakpoint-up(xxl) {
|
||||
.masonry-grid {
|
||||
column-count: 12;
|
||||
}
|
||||
}
|
||||
|
||||
.unguarded.btn-danger,
|
||||
.unguarded.btn-danger:hover,
|
||||
.unguarded.btn-danger:focus {
|
||||
|
|
|
|||
|
|
@ -6,14 +6,19 @@
|
|||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-header d-flex justify-content-between align-items-center flex-wrap">
|
||||
<h5 class="card-title mb-0">Results for "{{ query }}"</h5>
|
||||
<button v-if="layout === 'grid'" @click="layout = 'table'" class="btn">
|
||||
<b-icon-list></b-icon-list>
|
||||
</button>
|
||||
<button v-else @click="layout = 'grid'" class="btn">
|
||||
<b-icon-grid></b-icon-grid>
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button v-if="layout === 'grid'" @click="layout = 'masonry'" class="btn">
|
||||
<b-icon-columns-gap></b-icon-columns-gap>
|
||||
</button>
|
||||
<button v-else-if="layout === 'masonry'" @click="layout = 'table'" class="btn">
|
||||
<b-icon-list></b-icon-list>
|
||||
</button>
|
||||
<button v-else @click="layout = 'grid'" class="btn">
|
||||
<b-icon-grid></b-icon-grid>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-striped" v-if="layout === 'table'">
|
||||
<thead>
|
||||
|
|
@ -38,6 +43,29 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="card-body" v-else-if="layout === 'masonry'">
|
||||
<div class="masonry-grid">
|
||||
<div class="masonry-item" v-for="item in search_results" :key="item.id">
|
||||
<div class="card">
|
||||
<authenticated-image v-if="only_images(item.files).length > 0"
|
||||
:src="only_images(item.files)[0].name"
|
||||
:owner="only_images(item.files)[0].owner"
|
||||
class="card-img-top img-preview-fluid"/>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-0">
|
||||
<router-link :to="item.route">
|
||||
{{ item.name }}
|
||||
</router-link></h5>
|
||||
<div class="card-text text-black-50">
|
||||
<user-name-tag :user="item" :width="24" :height="24"/>
|
||||
<br>
|
||||
<span class="float-right">{{ item.owned_quantity }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body" v-else>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4 col-lg-3 col-xl-2" v-for="item in search_results"
|
||||
|
|
@ -46,7 +74,7 @@
|
|||
<authenticated-image v-if="only_images(item.files).length > 0"
|
||||
:src="only_images(item.files)[0].name"
|
||||
:owner="only_images(item.files)[0].owner"
|
||||
class="card-img-top img-preview"/>
|
||||
class="card-img-top img-preview-fixed"/>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title mb-0">
|
||||
<router-link :to="item.route">
|
||||
|
|
@ -71,7 +99,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions} from 'vuex';
|
||||
import {mapActions, mapGetters} from 'vuex';
|
||||
import * as BIcons from "bootstrap-icons-vue";
|
||||
import BaseLayout from "@/components/BaseLayout.vue";
|
||||
import SearchBox from "@/components/SearchBox.vue";
|
||||
|
|
@ -96,12 +124,23 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
search_results: [],
|
||||
layout: 'table'
|
||||
search_results: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getPreference']),
|
||||
// Implicit preference: remembers the last-used view without a settings-page entry. See docs/implementation.md#preferences.
|
||||
layout: {
|
||||
get() {
|
||||
return this.getPreference('ui.remembered.search_view_mode', 'grid')
|
||||
},
|
||||
set(value) {
|
||||
this.setDevicePreference({key: 'ui.remembered.search_view_mode', value})
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['fetchSearchResults']),
|
||||
...mapActions(['fetchSearchResults', 'setDevicePreference']),
|
||||
only_images(files) {
|
||||
if (!files) return [];
|
||||
return files.filter(file => file.mime_type.startsWith("image/"));
|
||||
|
|
@ -130,8 +169,3 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.img-preview {
|
||||
object-fit: cover;
|
||||
}
|
||||
</style>
|
||||
Loading…
Add table
Add a link
Reference in a new issue