Use components for photo details, list, mosaic and tile view #15
This commit is contained in:
parent
f9c553acb4
commit
e855262bb2
|
@ -134,228 +134,11 @@
|
||||||
<v-icon>delete</v-icon>
|
<v-icon>delete</v-icon>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</v-speed-dial>
|
</v-speed-dial>
|
||||||
<v-data-table
|
|
||||||
:headers="listColumns"
|
|
||||||
:items="results"
|
|
||||||
hide-actions
|
|
||||||
class="elevation-1"
|
|
||||||
v-if="query.view === 'list'"
|
|
||||||
select-all
|
|
||||||
disable-initial-sort
|
|
||||||
item-key="ID"
|
|
||||||
v-model="selected"
|
|
||||||
:no-data-text="'No photos matched your search'"
|
|
||||||
>
|
|
||||||
<template slot="items" slot-scope="props">
|
|
||||||
<td>
|
|
||||||
<v-checkbox
|
|
||||||
v-model="props.selected"
|
|
||||||
primary
|
|
||||||
hide-details
|
|
||||||
></v-checkbox>
|
|
||||||
</td>
|
|
||||||
<td>{{ props.item.PhotoTitle }}</td>
|
|
||||||
<td>{{ props.item.TakenAt | moment('DD/MM/YYYY hh:mm:ss') }}</td>
|
|
||||||
<td>{{ props.item.LocCity }}</td>
|
|
||||||
<td>{{ props.item.LocCountry }}</td>
|
|
||||||
<td>{{ props.item.CameraMake }} {{ props.item.CameraModel }}</td>
|
|
||||||
<td>{{ props.item.PhotoFavorite ? 'Yes' : 'No' }}</td>
|
|
||||||
</template>
|
|
||||||
</v-data-table>
|
|
||||||
|
|
||||||
<v-container grid-list-xs fluid class="pa-0" v-if="query.view === 'details'">
|
<app-photo-tiles v-if="query.view === 'tiles'" :photos="results" :open="openPhoto" :select="selectPhoto" :like="likePhoto"></app-photo-tiles>
|
||||||
<v-card v-if="results.length === 0">
|
<app-photo-mosaic v-if="query.view === 'mosaic'" :photos="results" :open="openPhoto" :select="selectPhoto" :like="likePhoto"></app-photo-mosaic>
|
||||||
<v-card-title primary-title>
|
<app-photo-details v-if="query.view === 'details'" :photos="results" :open="openPhoto" :select="selectPhoto" :like="likePhoto"></app-photo-details>
|
||||||
<div>
|
<app-photo-list v-if="query.view === 'list'" :photos="results" :selected-photos="selected" :open="openPhoto" :select="selectPhoto" :like="likePhoto"></app-photo-list>
|
||||||
<h3 class="headline mb-3">No photos matched your search</h3>
|
|
||||||
<div>Try using other terms and search options such as category, country and camera.</div>
|
|
||||||
</div>
|
|
||||||
</v-card-title>
|
|
||||||
</v-card>
|
|
||||||
<v-layout row wrap>
|
|
||||||
<v-flex
|
|
||||||
v-for="(photo, index) in results"
|
|
||||||
:key="photo.ID"
|
|
||||||
xs12 sm6 md4 lg3 d-flex
|
|
||||||
>
|
|
||||||
<v-hover>
|
|
||||||
<v-card tile slot-scope="{ hover }"
|
|
||||||
:dark="photo.selected"
|
|
||||||
:class="photo.selected ? 'elevation-14 ma-1' : 'elevation-2 ma-2'">
|
|
||||||
<v-img
|
|
||||||
:src="photo.getThumbnailUrl('tile_500')"
|
|
||||||
aspect-ratio="1"
|
|
||||||
v-bind:class="{ selected: photo.selected }"
|
|
||||||
style="cursor: pointer"
|
|
||||||
class="grey lighten-2"
|
|
||||||
@click="openPhoto(index)"
|
|
||||||
|
|
||||||
>
|
|
||||||
<v-layout
|
|
||||||
slot="placeholder"
|
|
||||||
fill-height
|
|
||||||
align-center
|
|
||||||
justify-center
|
|
||||||
ma-0
|
|
||||||
>
|
|
||||||
<v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular>
|
|
||||||
</v-layout>
|
|
||||||
|
|
||||||
<v-btn v-if="hover || photo.selected" :flat="!hover" icon large absolute
|
|
||||||
:ripple="false" style="right: 4px; bottom: 4px;"
|
|
||||||
@click.stop.prevent="selectPhoto(photo)">
|
|
||||||
<v-icon v-if="photo.selected" color="white">check_box</v-icon>
|
|
||||||
<v-icon v-else color="white">check_box_outline_blank</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
|
|
||||||
<v-btn v-if="hover || photo.PhotoFavorite" :flat="!hover" icon large absolute
|
|
||||||
:ripple="false" style="bottom: 4px; left: 4px"
|
|
||||||
@click.stop.prevent="likePhoto(photo)">
|
|
||||||
<v-icon v-if="photo.PhotoFavorite" color="white">favorite
|
|
||||||
</v-icon>
|
|
||||||
<v-icon v-else color="white">favorite_border</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</v-img>
|
|
||||||
|
|
||||||
|
|
||||||
<v-card-title primary-title class="pa-3">
|
|
||||||
<div>
|
|
||||||
<h3 class="subheading mb-2" :title="photo.PhotoTitle">{{ photo.PhotoTitle |
|
|
||||||
truncate(80) }}</h3>
|
|
||||||
<div class="caption">
|
|
||||||
<v-icon size="14">date_range</v-icon>
|
|
||||||
{{ photo.TakenAt | moment('DD/MM/YYYY hh:mm:ss') }}
|
|
||||||
<br/>
|
|
||||||
<v-icon size="14">photo_camera</v-icon>
|
|
||||||
{{ photo.getCamera() }}
|
|
||||||
<br/>
|
|
||||||
<v-icon size="14">location_on</v-icon>
|
|
||||||
<span class="link" :title="photo.getFullLocation()"
|
|
||||||
@click.stop="openLocation(photo)">{{ photo.getLocation() }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</v-card-title>
|
|
||||||
</v-card>
|
|
||||||
</v-hover>
|
|
||||||
</v-flex>
|
|
||||||
</v-layout>
|
|
||||||
</v-container>
|
|
||||||
|
|
||||||
<v-container grid-list-xs fluid class="pa-0" v-if="query.view === 'tiles'">
|
|
||||||
<v-card v-if="results.length === 0">
|
|
||||||
<v-card-title primary-title>
|
|
||||||
<div>
|
|
||||||
<h3 class="headline mb-3">No photos matched your search</h3>
|
|
||||||
<div>Try using other terms and search options such as category, country and camera.</div>
|
|
||||||
</div>
|
|
||||||
</v-card-title>
|
|
||||||
</v-card>
|
|
||||||
<v-layout row wrap>
|
|
||||||
<v-flex
|
|
||||||
v-for="(photo, index) in results"
|
|
||||||
:key="photo.ID"
|
|
||||||
xs12 sm6 md3 lg2 d-flex
|
|
||||||
v-bind:class="{ selected: photo.selected }"
|
|
||||||
>
|
|
||||||
<v-hover>
|
|
||||||
<v-card tile slot-scope="{ hover }"
|
|
||||||
:dark="photo.selected"
|
|
||||||
:class="photo.selected ? 'elevation-14 ma-1' : hover ? 'elevation-6 ma-2' : 'elevation-2 ma-2'">
|
|
||||||
<v-img :src="photo.getThumbnailUrl('tile_500')"
|
|
||||||
aspect-ratio="1"
|
|
||||||
class="grey lighten-2"
|
|
||||||
style="cursor: pointer"
|
|
||||||
@click="openPhoto(index)"
|
|
||||||
>
|
|
||||||
<v-layout
|
|
||||||
slot="placeholder"
|
|
||||||
fill-height
|
|
||||||
align-center
|
|
||||||
justify-center
|
|
||||||
ma-0
|
|
||||||
>
|
|
||||||
<v-progress-circular indeterminate
|
|
||||||
color="grey lighten-5"></v-progress-circular>
|
|
||||||
</v-layout>
|
|
||||||
|
|
||||||
<v-btn v-if="hover || photo.selected" :flat="!hover" icon large absolute
|
|
||||||
:ripple="false" style="right: 4px; bottom: 4px;"
|
|
||||||
@click.stop.prevent="selectPhoto(photo)">
|
|
||||||
<v-icon v-if="photo.selected" color="white">check_box</v-icon>
|
|
||||||
<v-icon v-else color="white">check_box_outline_blank</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
|
|
||||||
<v-btn v-if="hover || photo.PhotoFavorite" :flat="!hover" icon large absolute
|
|
||||||
:ripple="false" style="bottom: 4px; left: 4px"
|
|
||||||
@click.stop.prevent="likePhoto(photo)">
|
|
||||||
<v-icon v-if="photo.PhotoFavorite" color="white">favorite</v-icon>
|
|
||||||
<v-icon v-else color="white">favorite_border</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</v-img>
|
|
||||||
|
|
||||||
</v-card>
|
|
||||||
</v-hover>
|
|
||||||
</v-flex>
|
|
||||||
</v-layout>
|
|
||||||
</v-container>
|
|
||||||
|
|
||||||
<v-container grid-list-xs fluid class="pa-0" v-if="query.view === 'mosaic'">
|
|
||||||
<v-card v-if="results.length === 0">
|
|
||||||
<v-card-title primary-title>
|
|
||||||
<div>
|
|
||||||
<h3 class="headline mb-3">No photos matched your search</h3>
|
|
||||||
<div>Try using other terms and search options such as category, country and camera.</div>
|
|
||||||
</div>
|
|
||||||
</v-card-title>
|
|
||||||
</v-card>
|
|
||||||
<v-layout row wrap>
|
|
||||||
<v-flex
|
|
||||||
v-for="(photo, index) in results"
|
|
||||||
:key="photo.ID"
|
|
||||||
xs4 sm3 md2 lg1 d-flex
|
|
||||||
v-bind:class="{ selected: photo.selected }"
|
|
||||||
>
|
|
||||||
<v-hover>
|
|
||||||
<v-card tile slot-scope="{ hover }"
|
|
||||||
:dark="photo.selected"
|
|
||||||
:class="photo.selected ? 'elevation-14 ma-1' : hover ? 'elevation-6 ma-2' : 'elevation-2 ma-2'">
|
|
||||||
<v-img :src="photo.getThumbnailUrl('tile_224')"
|
|
||||||
aspect-ratio="1"
|
|
||||||
class="grey lighten-2"
|
|
||||||
style="cursor: pointer"
|
|
||||||
@click="openPhoto(index)"
|
|
||||||
>
|
|
||||||
<v-layout
|
|
||||||
slot="placeholder"
|
|
||||||
fill-height
|
|
||||||
align-center
|
|
||||||
justify-center
|
|
||||||
ma-0
|
|
||||||
>
|
|
||||||
<v-progress-circular indeterminate
|
|
||||||
color="grey lighten-5"></v-progress-circular>
|
|
||||||
</v-layout>
|
|
||||||
|
|
||||||
<v-btn v-if="hover || photo.selected" :flat="!hover" icon large absolute
|
|
||||||
:ripple="false" style="right: 1px; bottom: 1px;"
|
|
||||||
@click.stop.prevent="selectPhoto(photo)">
|
|
||||||
<v-icon v-if="photo.selected" color="white">check_box</v-icon>
|
|
||||||
<v-icon v-else color="white">check_box_outline_blank</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
|
|
||||||
<v-btn v-if="hover || photo.PhotoFavorite" :flat="!hover" icon large absolute
|
|
||||||
:ripple="false" style="bottom: 1px; left: 1px"
|
|
||||||
@click.stop.prevent="likePhoto(photo)">
|
|
||||||
<v-icon v-if="photo.PhotoFavorite" color="white">favorite</v-icon>
|
|
||||||
<v-icon v-else color="white">favorite_border</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</v-img>
|
|
||||||
|
|
||||||
</v-card>
|
|
||||||
</v-hover>
|
|
||||||
</v-flex>
|
|
||||||
</v-layout>
|
|
||||||
</v-container>
|
|
||||||
|
|
||||||
<v-snackbar
|
<v-snackbar
|
||||||
v-model="snackbarVisible"
|
v-model="snackbarVisible"
|
||||||
|
@ -609,14 +392,6 @@
|
||||||
{value: 'imported', text: 'Recently imported'},
|
{value: 'imported', text: 'Recently imported'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'listColumns': [
|
|
||||||
{text: 'Title', value: 'PhotoTitle'},
|
|
||||||
{text: 'Taken At', value: 'TakenAt'},
|
|
||||||
{text: 'City', value: 'LocCity'},
|
|
||||||
{text: 'Country', value: 'LocCountry'},
|
|
||||||
{text: 'Camera', value: 'CameraModel'},
|
|
||||||
{text: 'Favorite', value: 'PhotoFavorite'},
|
|
||||||
],
|
|
||||||
'view': view,
|
'view': view,
|
||||||
'loadMoreDisabled': true,
|
'loadMoreDisabled': true,
|
||||||
'pageSize': 60,
|
'pageSize': 60,
|
||||||
|
|
|
@ -12,10 +12,6 @@
|
||||||
</transition>
|
</transition>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
/**
|
|
||||||
* Helpers
|
|
||||||
*/
|
|
||||||
|
|
||||||
function clamp(n, min, max) {
|
function clamp(n, min, max) {
|
||||||
if (n < min) {
|
if (n < min) {
|
||||||
return min
|
return min
|
||||||
|
@ -27,22 +23,24 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let queue = (() => {
|
let queue = (() => {
|
||||||
let pending = []
|
let pending = [];
|
||||||
|
|
||||||
function next() {
|
function next() {
|
||||||
let fn = pending.shift()
|
let fn = pending.shift();
|
||||||
|
|
||||||
if (fn) {
|
if (fn) {
|
||||||
fn(next)
|
fn(next)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fn => {
|
return fn => {
|
||||||
pending.push(fn)
|
pending.push(fn);
|
||||||
|
|
||||||
if (pending.length === 1) {
|
if (pending.length === 1) {
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})()
|
})();
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'app-loading-bar',
|
name: 'app-loading-bar',
|
||||||
|
|
105
frontend/src/component/app-photo-details.vue
Normal file
105
frontend/src/component/app-photo-details.vue
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
<template>
|
||||||
|
<v-container grid-list-xs fluid class="pa-0">
|
||||||
|
<v-card v-if="photos.length === 0">
|
||||||
|
<v-card-title primary-title>
|
||||||
|
<div>
|
||||||
|
<h3 class="headline mb-3">No photos matched your search</h3>
|
||||||
|
<div>Try using other terms and search options such as category, country and camera.</div>
|
||||||
|
</div>
|
||||||
|
</v-card-title>
|
||||||
|
</v-card>
|
||||||
|
<v-layout row wrap>
|
||||||
|
<v-flex
|
||||||
|
v-for="(photo, index) in photos"
|
||||||
|
:key="photo.ID"
|
||||||
|
xs12 sm6 md4 lg3 d-flex
|
||||||
|
>
|
||||||
|
<v-hover>
|
||||||
|
<v-card tile slot-scope="{ hover }"
|
||||||
|
:dark="photo.selected"
|
||||||
|
:class="photo.selected ? 'elevation-14 ma-1' : 'elevation-2 ma-2'">
|
||||||
|
<v-img
|
||||||
|
:src="photo.getThumbnailUrl('tile_500')"
|
||||||
|
aspect-ratio="1"
|
||||||
|
v-bind:class="{ selected: photo.selected }"
|
||||||
|
style="cursor: pointer"
|
||||||
|
class="grey lighten-2"
|
||||||
|
@click="open(index)"
|
||||||
|
|
||||||
|
>
|
||||||
|
<v-layout
|
||||||
|
slot="placeholder"
|
||||||
|
fill-height
|
||||||
|
align-center
|
||||||
|
justify-center
|
||||||
|
ma-0
|
||||||
|
>
|
||||||
|
<v-progress-circular indeterminate color="grey lighten-5"></v-progress-circular>
|
||||||
|
</v-layout>
|
||||||
|
|
||||||
|
<v-btn v-if="hover || photo.selected" :flat="!hover" icon large absolute
|
||||||
|
:ripple="false" style="right: 4px; bottom: 4px;"
|
||||||
|
@click.stop.prevent="select(photo)">
|
||||||
|
<v-icon v-if="photo.selected" color="white">check_box</v-icon>
|
||||||
|
<v-icon v-else color="white">check_box_outline_blank</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
|
<v-btn v-if="hover || photo.PhotoFavorite" :flat="!hover" icon large absolute
|
||||||
|
:ripple="false" style="bottom: 4px; left: 4px"
|
||||||
|
@click.stop.prevent="like(photo)">
|
||||||
|
<v-icon v-if="photo.PhotoFavorite" color="white">favorite
|
||||||
|
</v-icon>
|
||||||
|
<v-icon v-else color="white">favorite_border</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-img>
|
||||||
|
|
||||||
|
|
||||||
|
<v-card-title primary-title class="pa-3">
|
||||||
|
<div>
|
||||||
|
<h3 class="subheading mb-2" :title="photo.PhotoTitle">{{ photo.PhotoTitle |
|
||||||
|
truncate(80) }}</h3>
|
||||||
|
<div class="caption">
|
||||||
|
<v-icon size="14">date_range</v-icon>
|
||||||
|
{{ photo.TakenAt | moment('DD/MM/YYYY hh:mm:ss') }}
|
||||||
|
<br/>
|
||||||
|
<v-icon size="14">photo_camera</v-icon>
|
||||||
|
{{ photo.getCamera() }}
|
||||||
|
<br/>
|
||||||
|
<v-icon size="14">location_on</v-icon>
|
||||||
|
<span class="link" :title="photo.getFullLocation()"
|
||||||
|
@click.stop="openLocation(photo)">{{ photo.getLocation() }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</v-card-title>
|
||||||
|
</v-card>
|
||||||
|
</v-hover>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'app-photo-details',
|
||||||
|
props: {
|
||||||
|
photos: Array,
|
||||||
|
open: Function,
|
||||||
|
select: Function,
|
||||||
|
like: Function,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
openLocation(photo) {
|
||||||
|
if (photo.PhotoLat && photo.PhotoLong) {
|
||||||
|
this.$router.push({name: 'Places', query: {lat: photo.PhotoLat, long: photo.PhotoLong}});
|
||||||
|
} else if (photo.LocName) {
|
||||||
|
this.$router.push({name: 'Places', query: {q: photo.LocName}});
|
||||||
|
} else if (photo.LocCity) {
|
||||||
|
this.$router.push({name: 'Places', query: {q: photo.LocCity}});
|
||||||
|
} else if (photo.LocCountry) {
|
||||||
|
this.$router.push({name: 'Places', query: {q: photo.LocCountry}});
|
||||||
|
} else {
|
||||||
|
this.$router.push({name: 'Places', query: {q: photo.CountryName}});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
55
frontend/src/component/app-photo-list.vue
Normal file
55
frontend/src/component/app-photo-list.vue
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
<template>
|
||||||
|
<v-data-table
|
||||||
|
:headers="listColumns"
|
||||||
|
:items="photos"
|
||||||
|
hide-actions
|
||||||
|
class="elevation-1"
|
||||||
|
select-all
|
||||||
|
disable-initial-sort
|
||||||
|
item-key="ID"
|
||||||
|
v-model="selectedPhotos"
|
||||||
|
:no-data-text="'No photos matched your search'"
|
||||||
|
>
|
||||||
|
<template slot="items" slot-scope="props">
|
||||||
|
<td>
|
||||||
|
<v-checkbox
|
||||||
|
v-model="props.selected"
|
||||||
|
primary
|
||||||
|
hide-details
|
||||||
|
></v-checkbox>
|
||||||
|
</td>
|
||||||
|
<td>{{ props.item.PhotoTitle }}</td>
|
||||||
|
<td>{{ props.item.TakenAt | moment('DD/MM/YYYY hh:mm:ss') }}</td>
|
||||||
|
<td>{{ props.item.LocCity }}</td>
|
||||||
|
<td>{{ props.item.LocCountry }}</td>
|
||||||
|
<td>{{ props.item.CameraMake }} {{ props.item.CameraModel }}</td>
|
||||||
|
<td>{{ props.item.PhotoFavorite ? 'Yes' : 'No' }}</td>
|
||||||
|
</template>
|
||||||
|
</v-data-table>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'app-photo-list',
|
||||||
|
props: {
|
||||||
|
photos: Array,
|
||||||
|
selectedPhotos: Array,
|
||||||
|
open: Function,
|
||||||
|
select: Function,
|
||||||
|
like: Function,
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
'listColumns': [
|
||||||
|
{text: 'Title', value: 'PhotoTitle'},
|
||||||
|
{text: 'Taken At', value: 'TakenAt'},
|
||||||
|
{text: 'City', value: 'LocCity'},
|
||||||
|
{text: 'Country', value: 'LocCountry'},
|
||||||
|
{text: 'Camera', value: 'CameraModel'},
|
||||||
|
{text: 'Favorite', value: 'PhotoFavorite'},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
72
frontend/src/component/app-photo-mosaic.vue
Normal file
72
frontend/src/component/app-photo-mosaic.vue
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<template>
|
||||||
|
<v-container grid-list-xs fluid class="pa-0 photo-mosaic">
|
||||||
|
<v-card v-if="photos.length === 0">
|
||||||
|
<v-card-title primary-title>
|
||||||
|
<div>
|
||||||
|
<h3 class="headline mb-3">No photos matched your search</h3>
|
||||||
|
<div>Try using other terms and search options such as category, country and camera.</div>
|
||||||
|
</div>
|
||||||
|
</v-card-title>
|
||||||
|
</v-card>
|
||||||
|
<v-layout row wrap>
|
||||||
|
<v-flex
|
||||||
|
v-for="(photo, index) in photos"
|
||||||
|
:key="photo.ID"
|
||||||
|
xs4 sm3 md2 lg1 d-flex
|
||||||
|
v-bind:class="{ selected: photo.selected }" class="single-photo"
|
||||||
|
>
|
||||||
|
<v-hover>
|
||||||
|
<v-card tile slot-scope="{ hover }"
|
||||||
|
:dark="photo.selected"
|
||||||
|
:class="photo.selected ? 'elevation-14 ma-1' : hover ? 'elevation-6 ma-2' : 'elevation-2 ma-2'">
|
||||||
|
<v-img :src="photo.getThumbnailUrl('tile_224')"
|
||||||
|
aspect-ratio="1"
|
||||||
|
class="grey lighten-2"
|
||||||
|
style="cursor: pointer"
|
||||||
|
@click="open(index)"
|
||||||
|
>
|
||||||
|
<v-layout
|
||||||
|
slot="placeholder"
|
||||||
|
fill-height
|
||||||
|
align-center
|
||||||
|
justify-center
|
||||||
|
ma-0
|
||||||
|
>
|
||||||
|
<v-progress-circular indeterminate
|
||||||
|
color="grey lighten-5"></v-progress-circular>
|
||||||
|
</v-layout>
|
||||||
|
|
||||||
|
<v-btn v-if="hover || photo.selected" :flat="!hover" icon large absolute
|
||||||
|
:ripple="false" style="right: 1px; bottom: 1px;"
|
||||||
|
@click.stop.prevent="select(photo)">
|
||||||
|
<v-icon v-if="photo.selected" color="white">check_box</v-icon>
|
||||||
|
<v-icon v-else color="white">check_box_outline_blank</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
|
<v-btn v-if="hover || photo.PhotoFavorite" :flat="!hover" icon large absolute
|
||||||
|
:ripple="false" style="bottom: 1px; left: 1px"
|
||||||
|
@click.stop.prevent="like(photo)">
|
||||||
|
<v-icon v-if="photo.PhotoFavorite" color="white">favorite</v-icon>
|
||||||
|
<v-icon v-else color="white">favorite_border</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-img>
|
||||||
|
|
||||||
|
</v-card>
|
||||||
|
</v-hover>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'app-photo-mosaic',
|
||||||
|
props: {
|
||||||
|
photos: Array,
|
||||||
|
open: Function,
|
||||||
|
select: Function,
|
||||||
|
like: Function,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
72
frontend/src/component/app-photo-tiles.vue
Normal file
72
frontend/src/component/app-photo-tiles.vue
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
<template>
|
||||||
|
<v-container grid-list-xs fluid class="pa-0">
|
||||||
|
<v-card v-if="photos.length === 0">
|
||||||
|
<v-card-title primary-title>
|
||||||
|
<div>
|
||||||
|
<h3 class="headline mb-3">No photos matched your search</h3>
|
||||||
|
<div>Try using other terms and search options such as category, country and camera.</div>
|
||||||
|
</div>
|
||||||
|
</v-card-title>
|
||||||
|
</v-card>
|
||||||
|
<v-layout row wrap>
|
||||||
|
<v-flex
|
||||||
|
v-for="(photo, index) in photos"
|
||||||
|
:key="photo.ID"
|
||||||
|
xs12 sm6 md3 lg2 d-flex
|
||||||
|
v-bind:class="{ selected: photo.selected }"
|
||||||
|
>
|
||||||
|
<v-hover>
|
||||||
|
<v-card tile slot-scope="{ hover }"
|
||||||
|
:dark="photo.selected"
|
||||||
|
:class="photo.selected ? 'elevation-14 ma-1' : hover ? 'elevation-6 ma-2' : 'elevation-2 ma-2'">
|
||||||
|
<v-img :src="photo.getThumbnailUrl('tile_500')"
|
||||||
|
aspect-ratio="1"
|
||||||
|
class="grey lighten-2"
|
||||||
|
style="cursor: pointer"
|
||||||
|
@click="open(index)"
|
||||||
|
>
|
||||||
|
<v-layout
|
||||||
|
slot="placeholder"
|
||||||
|
fill-height
|
||||||
|
align-center
|
||||||
|
justify-center
|
||||||
|
ma-0
|
||||||
|
>
|
||||||
|
<v-progress-circular indeterminate
|
||||||
|
color="grey lighten-5"></v-progress-circular>
|
||||||
|
</v-layout>
|
||||||
|
|
||||||
|
<v-btn v-if="hover || photo.selected" :flat="!hover" icon large absolute
|
||||||
|
:ripple="false" style="right: 4px; bottom: 4px;"
|
||||||
|
@click.stop.prevent="select(photo)">
|
||||||
|
<v-icon v-if="photo.selected" color="white">check_box</v-icon>
|
||||||
|
<v-icon v-else color="white">check_box_outline_blank</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
|
<v-btn v-if="hover || photo.PhotoFavorite" :flat="!hover" icon large absolute
|
||||||
|
:ripple="false" style="bottom: 4px; left: 4px"
|
||||||
|
@click.stop.prevent="like(photo)">
|
||||||
|
<v-icon v-if="photo.PhotoFavorite" color="white">favorite</v-icon>
|
||||||
|
<v-icon v-else color="white">favorite_border</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</v-img>
|
||||||
|
|
||||||
|
</v-card>
|
||||||
|
</v-hover>
|
||||||
|
</v-flex>
|
||||||
|
</v-layout>
|
||||||
|
</v-container>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'app-photo-tiles',
|
||||||
|
props: {
|
||||||
|
photos: Array,
|
||||||
|
open: Function,
|
||||||
|
select: Function,
|
||||||
|
like: Function,
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -2,6 +2,10 @@ import AppAlert from "./app-alert.vue";
|
||||||
import AppNavigation from "./app-navigation.vue";
|
import AppNavigation from "./app-navigation.vue";
|
||||||
import AppLoadingBar from "./app-loading-bar.vue";
|
import AppLoadingBar from "./app-loading-bar.vue";
|
||||||
import AppGallery from "./app-gallery.vue";
|
import AppGallery from "./app-gallery.vue";
|
||||||
|
import AppPhotoDetails from "./app-photo-details.vue";
|
||||||
|
import AppPhotoTiles from "./app-photo-tiles.vue";
|
||||||
|
import AppPhotoMosaic from "./app-photo-mosaic.vue";
|
||||||
|
import AppPhotoList from "./app-photo-list.vue";
|
||||||
|
|
||||||
const components = {};
|
const components = {};
|
||||||
|
|
||||||
|
@ -10,6 +14,10 @@ components.install = (Vue) => {
|
||||||
Vue.component("app-gallery", AppGallery);
|
Vue.component("app-gallery", AppGallery);
|
||||||
Vue.component("app-navigation", AppNavigation);
|
Vue.component("app-navigation", AppNavigation);
|
||||||
Vue.component("app-loading-bar", AppLoadingBar);
|
Vue.component("app-loading-bar", AppLoadingBar);
|
||||||
|
Vue.component("app-photo-details", AppPhotoDetails);
|
||||||
|
Vue.component("app-photo-tiles", AppPhotoTiles);
|
||||||
|
Vue.component("app-photo-mosaic", AppPhotoMosaic);
|
||||||
|
Vue.component("app-photo-list", AppPhotoList);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default components;
|
export default components;
|
||||||
|
|
|
@ -88,7 +88,6 @@ const config = {
|
||||||
hmr: false,
|
hmr: false,
|
||||||
fallback: "vue-style-loader",
|
fallback: "vue-style-loader",
|
||||||
use: [
|
use: [
|
||||||
// "vue-style-loader",
|
|
||||||
"style-loader",
|
"style-loader",
|
||||||
{
|
{
|
||||||
loader: "css-loader",
|
loader: "css-loader",
|
||||||
|
|
Loading…
Reference in a new issue