Frontend: Refactor clipboard #477
This commit is contained in:
parent
39595ee34d
commit
ec71858477
13 changed files with 123 additions and 69 deletions
|
@ -59,7 +59,6 @@ import offline from "offline-plugin/runtime";
|
|||
|
||||
// Initialize helpers
|
||||
const viewer = new Viewer();
|
||||
const clipboard = new Clipboard(window.localStorage, "photo_clipboard");
|
||||
const isPublic = config.get("public");
|
||||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
||||
navigator.userAgent
|
||||
|
@ -83,7 +82,7 @@ Vue.prototype.$api = Api;
|
|||
Vue.prototype.$log = Log;
|
||||
Vue.prototype.$socket = Socket;
|
||||
Vue.prototype.$config = config;
|
||||
Vue.prototype.$clipboard = clipboard;
|
||||
Vue.prototype.$clipboard = Clipboard;
|
||||
Vue.prototype.$isMobile = isMobile;
|
||||
Vue.prototype.$rtl = rtl;
|
||||
|
||||
|
|
|
@ -31,10 +31,11 @@ https://docs.photoprism.org/developer-guide/
|
|||
import RestModel from "model/rest";
|
||||
import Notify from "common/notify";
|
||||
import { $gettext } from "./vm";
|
||||
import Event from "pubsub-js";
|
||||
|
||||
export const MaxItems = 999;
|
||||
|
||||
export default class Clipboard {
|
||||
export class Clipboard {
|
||||
/**
|
||||
* @param {Storage} storage
|
||||
* @param {string} key
|
||||
|
@ -83,28 +84,37 @@ export default class Clipboard {
|
|||
}
|
||||
|
||||
const id = model.getId();
|
||||
this.toggleId(id);
|
||||
model.Selected = this.toggleId(id);
|
||||
}
|
||||
|
||||
toggleId(id) {
|
||||
const index = this.selection.indexOf(id);
|
||||
|
||||
let result = false;
|
||||
|
||||
if (index === -1) {
|
||||
if (this.selection.length >= this.maxItems) {
|
||||
Notify.warn($gettext("Can't select more items"));
|
||||
return;
|
||||
}
|
||||
|
||||
Event.publish("photos.updated", { entities: [{ UID: id, Selected: true }] });
|
||||
|
||||
this.selection.push(id);
|
||||
this.selectionMap["id:" + id] = true;
|
||||
this.lastId = id;
|
||||
result = true;
|
||||
} else {
|
||||
Event.publish("photos.updated", { entities: [{ UID: id, Selected: false }] });
|
||||
|
||||
this.selection.splice(index, 1);
|
||||
delete this.selectionMap["id:" + id];
|
||||
this.lastId = "";
|
||||
}
|
||||
|
||||
this.saveToStorage();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
add(model) {
|
||||
|
@ -114,17 +124,19 @@ export default class Clipboard {
|
|||
|
||||
const id = model.getId();
|
||||
|
||||
this.addId(id);
|
||||
model.Selected = this.addId(id);
|
||||
}
|
||||
|
||||
addId(id) {
|
||||
Event.publish("photos.updated", { entities: [{ UID: id, Selected: true }] });
|
||||
|
||||
if (this.hasId(id)) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.selection.length >= this.maxItems) {
|
||||
Notify.warn($gettext("Can't select more items"));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.selection.push(id);
|
||||
|
@ -132,6 +144,8 @@ export default class Clipboard {
|
|||
this.lastId = id;
|
||||
|
||||
this.saveToStorage();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
addRange(rangeEnd, models) {
|
||||
|
@ -177,11 +191,15 @@ export default class Clipboard {
|
|||
return;
|
||||
}
|
||||
|
||||
this.removeId(model.getId());
|
||||
model.Selected = this.removeId(model.getId());
|
||||
}
|
||||
|
||||
removeId(id) {
|
||||
if (!this.hasId(id)) return;
|
||||
Event.publish("photos.updated", { entities: [{ UID: id, Selected: false }] });
|
||||
|
||||
if (!this.hasId(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const index = this.selection.indexOf(id);
|
||||
|
||||
|
@ -190,6 +208,8 @@ export default class Clipboard {
|
|||
delete this.selectionMap["id:" + id];
|
||||
|
||||
this.saveToStorage();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
getIds() {
|
||||
|
@ -209,9 +229,19 @@ export default class Clipboard {
|
|||
}
|
||||
|
||||
clear() {
|
||||
Event.publish("photos.updated", {
|
||||
entities: this.selection.map((uid) => {
|
||||
return { UID: uid, Selected: false };
|
||||
}),
|
||||
});
|
||||
|
||||
this.lastId = "";
|
||||
this.selectionMap = {};
|
||||
this.selection.splice(0, this.selection.length);
|
||||
this.storage.removeItem(this.storageKey);
|
||||
}
|
||||
}
|
||||
|
||||
const PhotoClipboard = new Clipboard(window.localStorage, "photo_clipboard");
|
||||
|
||||
export default PhotoClipboard;
|
||||
|
|
|
@ -26,30 +26,30 @@
|
|||
:data-uid="photo.UID"
|
||||
class="p-photo"
|
||||
xs12 sm6 md4 lg3 xlg2 xxxl1 d-flex
|
||||
:class="{ 'is-selected': clipboard.has(photo), portrait: photo.Portrait }"
|
||||
:class="{ 'is-selected': photo.Selected, portrait: photo.Portrait }"
|
||||
>
|
||||
<v-hover>
|
||||
<v-card slot-scope="{ hover }" tile
|
||||
:dark="clipboard.has(photo)"
|
||||
:class="clipboard.has(photo) ? 'elevation-10 ma-0 accent darken-1 white--text' : 'elevation-0 ma-1 accent lighten-3'"
|
||||
:dark="photo.Selected"
|
||||
:class="photo.Selected ? 'elevation-10 ma-0 accent darken-1 white--text select-transition' : 'elevation-0 ma-1 accent lighten-3 select-transition'"
|
||||
@contextmenu="onContextMenu($event, index)">
|
||||
<v-img :src="photo.thumbnailUrl('tile_500')"
|
||||
aspect-ratio="1"
|
||||
:class="{ selected: clipboard.has(photo) }"
|
||||
:class="{ selected: photo.Selected }"
|
||||
class="accent lighten-2 clickable"
|
||||
@mousedown="onMouseDown($event, index)"
|
||||
@click.stop.prevent="onClick($event, index)"
|
||||
>
|
||||
<v-layout
|
||||
<!-- v-layout
|
||||
v-if="spinners"
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
justify-center
|
||||
ma-0
|
||||
|
||||
>
|
||||
<v-progress-circular indeterminate color="accent lighten-5"></v-progress-circular>
|
||||
</v-layout>
|
||||
</v-layout -->
|
||||
|
||||
<v-layout
|
||||
v-if="photo.Type === 'live'"
|
||||
|
@ -72,11 +72,11 @@
|
|||
<v-icon color="white">lock</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="hover || selection.length && clipboard.has(photo)" :ripple="false"
|
||||
<v-btn v-if="hover || photo.Selected" :ripple="false"
|
||||
icon flat large absolute
|
||||
:class="selection.length && clipboard.has(photo) ? 'p-photo-select' : 'p-photo-select opacity-50'"
|
||||
:class="photo.Selected ? 'p-photo-select' : 'p-photo-select opacity-50'"
|
||||
@click.stop.prevent="onSelect($event, index)">
|
||||
<v-icon v-if="selection.length && clipboard.has(photo)" color="white"
|
||||
<v-icon v-if="photo.Selected" color="white"
|
||||
class="t-select t-on">check_circle
|
||||
</v-icon>
|
||||
<v-icon v-else color="accent lighten-3" class="t-select t-off">radio_button_off</v-icon>
|
||||
|
@ -110,7 +110,7 @@
|
|||
@click.stop.prevent="openPhoto(index, true)">
|
||||
<v-icon color="white" class="action-burst">burst_mode</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="photo.Type === 'image' && selection.length && hover" :ripple="false"
|
||||
<v-btn v-else-if="photo.Type === 'image' && selectMode && hover" :ripple="false"
|
||||
icon flat large absolute class="p-photo-fullscreen opacity-75"
|
||||
@click.stop.prevent="openPhoto(index, false)">
|
||||
<v-icon color="white" class="action-open">zoom_in</v-icon>
|
||||
|
@ -207,10 +207,11 @@ export default {
|
|||
album: Object,
|
||||
filter: Object,
|
||||
context: String,
|
||||
selectMode: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
clipboard: this.$clipboard,
|
||||
spinners: false,
|
||||
showLocation: this.$config.settings().features.places,
|
||||
hidePrivate: this.$config.settings().features.private,
|
||||
debug: this.$config.get('debug'),
|
||||
|
@ -246,7 +247,7 @@ export default {
|
|||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
if (longClick || this.selectMode) {
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index);
|
||||
} else {
|
||||
|
|
|
@ -39,7 +39,8 @@
|
|||
@contextmenu="onContextMenu($event, props.index)"
|
||||
@click.stop.prevent="onClick($event, props.index)"
|
||||
>
|
||||
<v-layout
|
||||
<!-- v-layout
|
||||
v-if="spinners"
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
|
@ -48,13 +49,13 @@
|
|||
>
|
||||
<v-progress-circular indeterminate
|
||||
color="accent lighten-5"></v-progress-circular>
|
||||
</v-layout>
|
||||
</v-layout -->
|
||||
|
||||
<v-btn v-if="selection.length && clipboard.has(props.item)" :ripple="false"
|
||||
<v-btn v-if="props.item.Selected" :ripple="false"
|
||||
flat icon large absolute class="p-photo-select">
|
||||
<v-icon color="white" class="t-select t-on">check_circle</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="!selection.length && (props.item.Type === 'video' || props.item.Type === 'live')"
|
||||
<v-btn v-else-if="!selectMode && (props.item.Type === 'video' || props.item.Type === 'live')"
|
||||
:ripple="false"
|
||||
flat icon large absolute class="p-photo-play opacity-75"
|
||||
@click.stop.prevent="openPhoto(props.index, true)">
|
||||
|
@ -112,13 +113,13 @@ export default {
|
|||
name: 'PPhotoList',
|
||||
props: {
|
||||
photos: Array,
|
||||
selection: Array,
|
||||
openPhoto: Function,
|
||||
editPhoto: Function,
|
||||
openLocation: Function,
|
||||
album: Object,
|
||||
filter: Object,
|
||||
context: String,
|
||||
selectMode: Boolean,
|
||||
},
|
||||
data() {
|
||||
let m = this.$gettext("Couldn't find anything.");
|
||||
|
@ -132,7 +133,7 @@ export default {
|
|||
let showName = this.filter.order === 'name';
|
||||
|
||||
return {
|
||||
clipboard: this.$clipboard,
|
||||
spinners: false,
|
||||
config: this.$config.values,
|
||||
notFoundMessage: m,
|
||||
'selected': [],
|
||||
|
@ -199,7 +200,7 @@ export default {
|
|||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
if (longClick || this.selectMode) {
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index);
|
||||
} else {
|
||||
|
|
|
@ -24,13 +24,13 @@
|
|||
v-for="(photo, index) in photos"
|
||||
:key="index"
|
||||
:data-uid="photo.UID"
|
||||
:class="{ selected: $clipboard.has(photo), portrait: photo.Portrait }"
|
||||
:class="{ selected: photo.Selected, portrait: photo.Portrait }"
|
||||
class="p-photo"
|
||||
xs4 sm3 md2 lg1 d-flex
|
||||
>
|
||||
<v-hover>
|
||||
<v-card slot-scope="{ hover }" tile
|
||||
:class="$clipboard.has(photo) ? 'elevation-10 ma-0' : 'elevation-0 ma-1'"
|
||||
:class="photo.Selected ? 'elevation-10 ma-0 select-transition' : 'elevation-0 ma-1 select-transition'"
|
||||
:title="photo.Title"
|
||||
@contextmenu="onContextMenu($event, index)">
|
||||
<v-img :src="photo.thumbnailUrl('tile_224')"
|
||||
|
@ -39,7 +39,8 @@
|
|||
@mousedown="onMouseDown($event, index)"
|
||||
@click.stop.prevent="onClick($event, index)"
|
||||
>
|
||||
<v-layout
|
||||
<!-- v-layout
|
||||
v-if="spinners"
|
||||
slot="placeholder"
|
||||
fill-height
|
||||
align-center
|
||||
|
@ -48,7 +49,7 @@
|
|||
>
|
||||
<v-progress-circular indeterminate
|
||||
color="accent lighten-5"></v-progress-circular>
|
||||
</v-layout>
|
||||
</v-layout -->
|
||||
|
||||
<v-layout
|
||||
v-if="photo.Type === 'live'"
|
||||
|
@ -71,11 +72,11 @@
|
|||
<v-icon color="white">lock</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="hover || selection.length && $clipboard.has(photo)" :ripple="false"
|
||||
<v-btn v-if="hover || photo.Selected" :ripple="false"
|
||||
icon flat small absolute
|
||||
:class="selection.length && $clipboard.has(photo) ? 'p-photo-select' : 'p-photo-select opacity-50'"
|
||||
:class="photo.Selected ? 'p-photo-select' : 'p-photo-select opacity-50'"
|
||||
@click.stop.prevent="onSelect($event, index)">
|
||||
<v-icon v-if="selection.length && $clipboard.has(photo)" color="white"
|
||||
<v-icon v-if="photo.Selected" color="white"
|
||||
class="t-select t-on">check_circle
|
||||
</v-icon>
|
||||
<v-icon v-else color="accent lighten-3" class="t-select t-off">radio_button_off</v-icon>
|
||||
|
@ -106,7 +107,7 @@
|
|||
@click.stop.prevent="openPhoto(index, true)">
|
||||
<v-icon color="white" class="action-burst">burst_mode</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="photo.Type === 'image' && selection.length && hover" :ripple="false"
|
||||
<v-btn v-else-if="photo.Type === 'image' && selectMode && hover" :ripple="false"
|
||||
icon flat small absolute class="p-photo-fullscreen opacity-75"
|
||||
@click.stop.prevent="openPhoto(index, false)">
|
||||
<v-icon color="white" class="action-open">zoom_in</v-icon>
|
||||
|
@ -128,15 +129,16 @@ export default {
|
|||
name: 'PPhotoMosaic',
|
||||
props: {
|
||||
photos: Array,
|
||||
selection: Array,
|
||||
openPhoto: Function,
|
||||
editPhoto: Function,
|
||||
album: Object,
|
||||
filter: Object,
|
||||
context: String,
|
||||
selectMode: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
spinners: false,
|
||||
hidePrivate: this.$config.settings().features.private,
|
||||
mouseDown: {
|
||||
index: -1,
|
||||
|
@ -159,7 +161,7 @@ export default {
|
|||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
if (longClick || this.selectMode) {
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index);
|
||||
} else {
|
||||
|
|
7
frontend/src/css/animate.css
vendored
7
frontend/src/css/animate.css
vendored
|
@ -92,3 +92,10 @@
|
|||
#photoprism .animate-stretch {
|
||||
animation: stretch 1.5s ease-out 0s alternate infinite none running;
|
||||
}
|
||||
|
||||
#photoprism .select-transition {
|
||||
-webkit-transition-duration: 15ms !important;
|
||||
-moz-transition-duration: 15ms !important;
|
||||
-o-transition-duration: 15ms !important;
|
||||
transition-duration: 15ms !important;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import Util from "common/util";
|
|||
import { config } from "../session";
|
||||
import countries from "options/countries.json";
|
||||
import { $gettext } from "common/vm";
|
||||
import Clipboard from "common/clipboard";
|
||||
|
||||
export const SrcManual = "manual";
|
||||
export const CodecAvc1 = "avc1";
|
||||
|
@ -51,8 +52,14 @@ export const MonthUnknown = -1;
|
|||
export const DayUnknown = -1;
|
||||
|
||||
export class Photo extends RestModel {
|
||||
constructor(values) {
|
||||
super(values);
|
||||
this.Selected = Clipboard.has(this);
|
||||
}
|
||||
|
||||
getDefaults() {
|
||||
return {
|
||||
Selected: false,
|
||||
UID: "",
|
||||
DocumentID: "",
|
||||
Type: TypeImage,
|
||||
|
|
|
@ -16,14 +16,14 @@
|
|||
<p-photo-mosaic v-if="settings.view === 'mosaic'"
|
||||
:context="context"
|
||||
:photos="results"
|
||||
:selection="selection"
|
||||
:select-mode="selectMode"
|
||||
:filter="filter"
|
||||
:edit-photo="editPhoto"
|
||||
:open-photo="openPhoto"></p-photo-mosaic>
|
||||
<p-photo-list v-else-if="settings.view === 'list'"
|
||||
:context="context"
|
||||
:photos="results"
|
||||
:selection="selection"
|
||||
:select-mode="selectMode"
|
||||
:filter="filter"
|
||||
:open-photo="openPhoto"
|
||||
:edit-photo="editPhoto"
|
||||
|
@ -31,7 +31,7 @@
|
|||
<p-photo-cards v-else
|
||||
:context="context"
|
||||
:photos="results"
|
||||
:selection="selection"
|
||||
:select-mode="selectMode"
|
||||
:filter="filter"
|
||||
:open-photo="openPhoto"
|
||||
:edit-photo="editPhoto"
|
||||
|
@ -108,6 +108,9 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
selectMode: function() {
|
||||
return this.selection.length > 0;
|
||||
},
|
||||
context: function () {
|
||||
if (!this.staticFilter) {
|
||||
return "photos";
|
||||
|
|
|
@ -58,7 +58,6 @@ import * as options from "./options/options";
|
|||
|
||||
// Initialize helpers
|
||||
const viewer = new Viewer();
|
||||
const clipboard = new Clipboard(window.localStorage, "photo_clipboard");
|
||||
const isPublic = config.get("public");
|
||||
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
||||
navigator.userAgent
|
||||
|
@ -82,7 +81,7 @@ Vue.prototype.$api = Api;
|
|||
Vue.prototype.$log = Log;
|
||||
Vue.prototype.$socket = Socket;
|
||||
Vue.prototype.$config = config;
|
||||
Vue.prototype.$clipboard = clipboard;
|
||||
Vue.prototype.$clipboard = Clipboard;
|
||||
Vue.prototype.$isMobile = isMobile;
|
||||
Vue.prototype.$rtl = rtl;
|
||||
|
||||
|
|
|
@ -22,16 +22,16 @@
|
|||
:data-uid="photo.UID"
|
||||
class="p-photo"
|
||||
xs12 sm6 md4 lg3 xlg2 xxxl1 d-flex
|
||||
:class="{ 'is-selected': $clipboard.has(photo), portrait: photo.Portrait }"
|
||||
:class="{ 'is-selected': photo.Selected, portrait: photo.Portrait }"
|
||||
>
|
||||
<v-hover>
|
||||
<v-card slot-scope="{ hover }" tile
|
||||
:dark="$clipboard.has(photo)"
|
||||
:class="$clipboard.has(photo) ? 'elevation-10 ma-0 accent darken-1 white--text' : 'elevation-0 ma-1 accent lighten-3'"
|
||||
:dark="photo.Selected"
|
||||
:class="photo.Selected ? 'elevation-10 ma-0 accent darken-1 white--text select-transition' : 'elevation-0 ma-1 accent lighten-3 select-transition'"
|
||||
@contextmenu="onContextMenu($event, index)">
|
||||
<v-img :src="photo.thumbnailUrl('tile_500')"
|
||||
aspect-ratio="1"
|
||||
:class="{ selected: $clipboard.has(photo) }"
|
||||
:class="{ selected: photo.Selected }"
|
||||
class="accent lighten-2 clickable"
|
||||
@mousedown="onMouseDown($event, index)"
|
||||
@click.stop.prevent="onClick($event, index)"
|
||||
|
@ -68,11 +68,11 @@
|
|||
<v-icon color="white">lock</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="hover || selection.length && $clipboard.has(photo)" :ripple="false"
|
||||
<v-btn v-if="hover || photo.Selected" :ripple="false"
|
||||
icon flat large absolute
|
||||
:class="selection.length && $clipboard.has(photo) ? 'p-photo-select' : 'p-photo-select opacity-50'"
|
||||
:class="photo.Selected ? 'p-photo-select' : 'p-photo-select opacity-50'"
|
||||
@click.stop.prevent="onSelect($event, index)">
|
||||
<v-icon v-if="selection.length && $clipboard.has(photo)" color="white"
|
||||
<v-icon v-if="photo.Selected" color="white"
|
||||
class="t-select t-on">check_circle
|
||||
</v-icon>
|
||||
<v-icon v-else color="accent lighten-3" class="t-select t-off">radio_button_off</v-icon>
|
||||
|
@ -106,7 +106,7 @@
|
|||
@click.stop.prevent="openPhoto(index, true)">
|
||||
<v-icon color="white" class="action-burst">burst_mode</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="photo.Type === 'image' && selection.length && hover" :ripple="false"
|
||||
<v-btn v-else-if="photo.Type === 'image' && selectMode && hover" :ripple="false"
|
||||
icon flat large absolute class="p-photo-merged opacity-75"
|
||||
@click.stop.prevent="openPhoto(index, false)">
|
||||
<v-icon color="white" class="action-open">zoom_in</v-icon>
|
||||
|
@ -162,12 +162,12 @@ export default {
|
|||
name: 'PPhotoCards',
|
||||
props: {
|
||||
photos: Array,
|
||||
selection: Array,
|
||||
openPhoto: Function,
|
||||
editPhoto: Function,
|
||||
openLocation: Function,
|
||||
album: Object,
|
||||
filter: Object,
|
||||
selectMode: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -202,7 +202,7 @@ export default {
|
|||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
if (longClick || this.selectMode) {
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index);
|
||||
} else {
|
||||
|
|
|
@ -46,11 +46,11 @@
|
|||
color="accent lighten-5"></v-progress-circular>
|
||||
</v-layout>
|
||||
|
||||
<v-btn v-if="selection.length && $clipboard.has(props.item)" :ripple="false"
|
||||
<v-btn v-if="props.item.Selected" :ripple="false"
|
||||
flat icon large absolute class="p-photo-select">
|
||||
<v-icon color="white" class="t-select t-on">check_circle</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="!selection.length && (props.item.Type === 'video' || props.item.Type === 'live')"
|
||||
<v-btn v-else-if="!selectMode && (props.item.Type === 'video' || props.item.Type === 'live')"
|
||||
:ripple="false"
|
||||
flat icon large absolute class="p-photo-play opacity-75"
|
||||
@click.stop.prevent="openPhoto(props.index, true)">
|
||||
|
@ -93,12 +93,12 @@ export default {
|
|||
name: 'PPhotoList',
|
||||
props: {
|
||||
photos: Array,
|
||||
selection: Array,
|
||||
openPhoto: Function,
|
||||
editPhoto: Function,
|
||||
openLocation: Function,
|
||||
album: Object,
|
||||
filter: Object,
|
||||
selectMode: Boolean,
|
||||
},
|
||||
data() {
|
||||
let m = this.$gettext("Couldn't find anything.");
|
||||
|
@ -172,7 +172,7 @@ export default {
|
|||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
if (longClick || this.selectMode) {
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index);
|
||||
} else {
|
||||
|
|
|
@ -20,13 +20,13 @@
|
|||
v-for="(photo, index) in photos"
|
||||
:key="index"
|
||||
:data-uid="photo.UID"
|
||||
:class="{ selected: $clipboard.has(photo), portrait: photo.Portrait }"
|
||||
:class="{ selected: photo.Selected, portrait: photo.Portrait }"
|
||||
class="p-photo"
|
||||
xs4 sm3 md2 lg1 d-flex
|
||||
>
|
||||
<v-hover>
|
||||
<v-card slot-scope="{ hover }" tile
|
||||
:class="$clipboard.has(photo) ? 'elevation-10 ma-0' : 'elevation-0 ma-1'"
|
||||
:class="photo.Selected ? 'elevation-10 ma-0 select-transition' : 'elevation-0 ma-1 select-transition'"
|
||||
:title="photo.Title"
|
||||
@contextmenu="onContextMenu($event, index)">
|
||||
<v-img :src="photo.thumbnailUrl('tile_224')"
|
||||
|
@ -67,11 +67,11 @@
|
|||
<v-icon color="white">lock</v-icon>
|
||||
</v-btn>
|
||||
|
||||
<v-btn v-if="hover || selection.length && $clipboard.has(photo)" :ripple="false"
|
||||
<v-btn v-if="hover || photo.Selected" :ripple="false"
|
||||
icon flat small absolute
|
||||
:class="selection.length && $clipboard.has(photo) ? 'p-photo-select' : 'p-photo-select opacity-50'"
|
||||
:class="photo.Selected ? 'p-photo-select' : 'p-photo-select opacity-50'"
|
||||
@click.stop.prevent="onSelect($event, index)">
|
||||
<v-icon v-if="selection.length && $clipboard.has(photo)" color="white"
|
||||
<v-icon v-if="photo.Selected" color="white"
|
||||
class="t-select t-on">check_circle
|
||||
</v-icon>
|
||||
<v-icon v-else color="accent lighten-3" class="t-select t-off">radio_button_off</v-icon>
|
||||
|
@ -101,7 +101,7 @@
|
|||
@click.stop.prevent="openPhoto(index, true)">
|
||||
<v-icon color="white" class="action-burst">burst_mode</v-icon>
|
||||
</v-btn>
|
||||
<v-btn v-else-if="photo.Type === 'image' && selection.length && hover" :ripple="false"
|
||||
<v-btn v-else-if="photo.Type === 'image' && selectMode && hover" :ripple="false"
|
||||
icon flat small absolute class="p-photo-merged opacity-75"
|
||||
@click.stop.prevent="openPhoto(index, false)">
|
||||
<v-icon color="white" class="action-open">zoom_in</v-icon>
|
||||
|
@ -123,11 +123,11 @@ export default {
|
|||
name: 'PPhotoMosaic',
|
||||
props: {
|
||||
photos: Array,
|
||||
selection: Array,
|
||||
openPhoto: Function,
|
||||
editPhoto: Function,
|
||||
album: Object,
|
||||
filter: Object,
|
||||
selectMode: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -153,7 +153,7 @@ export default {
|
|||
onClick(ev, index) {
|
||||
let longClick = (this.mouseDown.index === index && ev.timeStamp - this.mouseDown.timeStamp > 400);
|
||||
|
||||
if (longClick || this.selection.length > 0) {
|
||||
if (longClick || this.selectMode) {
|
||||
if (longClick || ev.shiftKey) {
|
||||
this.selectRange(index);
|
||||
} else {
|
||||
|
|
|
@ -60,14 +60,14 @@
|
|||
|
||||
<p-photo-mosaic v-if="settings.view === 'mosaic'"
|
||||
:photos="results"
|
||||
:selection="selection"
|
||||
:select-mode="selectMode"
|
||||
:filter="filter"
|
||||
:album="model"
|
||||
:edit-photo="editPhoto"
|
||||
:open-photo="openPhoto"></p-photo-mosaic>
|
||||
<p-photo-list v-else-if="settings.view === 'list'"
|
||||
:photos="results"
|
||||
:selection="selection"
|
||||
:select-mode="selectMode"
|
||||
:filter="filter"
|
||||
:album="model"
|
||||
:open-photo="openPhoto"
|
||||
|
@ -75,7 +75,7 @@
|
|||
:open-location="openLocation"></p-photo-list>
|
||||
<p-photo-cards v-else
|
||||
:photos="results"
|
||||
:selection="selection"
|
||||
:select-mode="selectMode"
|
||||
:filter="filter"
|
||||
:album="model"
|
||||
:open-photo="openPhoto"
|
||||
|
@ -134,6 +134,11 @@ export default {
|
|||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
selectMode: function() {
|
||||
return this.$clipboard.selection.length > 0;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$route'() {
|
||||
const query = this.$route.query;
|
||||
|
|
Loading…
Reference in a new issue