|
@@ -21,34 +21,29 @@
|
|
</v-alert>
|
|
</v-alert>
|
|
</template>
|
|
</template>
|
|
<v-layout row wrap class="search-results photo-results cards-view" :class="{'select-results': selectMode}">
|
|
<v-layout row wrap class="search-results photo-results cards-view" :class="{'select-results': selectMode}">
|
|
- <v-flex
|
|
|
|
|
|
+ <div
|
|
v-for="(photo, index) in photos"
|
|
v-for="(photo, index) in photos"
|
|
ref="items"
|
|
ref="items"
|
|
:key="photo.ID"
|
|
:key="photo.ID"
|
|
:data-index="index"
|
|
:data-index="index"
|
|
- style="width: min-content"
|
|
|
|
- xs12 sm6 md4 lg3 xlg2 xxxl1 d-flex
|
|
|
|
|
|
+ class="flex xs12 sm6 md4 lg3 xlg2 xxxl1 d-flex"
|
|
>
|
|
>
|
|
- <div v-if="index < firstVisibleElementIndex || index > lastVisibileElementIndex"
|
|
|
|
- style="user-select: none"
|
|
|
|
- class="accent lighten-3 result"
|
|
|
|
- :class="photo.classes()"
|
|
|
|
- >
|
|
|
|
- <div class="accent lighten-2" style="aspect-ratio: 1" />
|
|
|
|
|
|
+ <div v-if="index < firstVisibleElementIndex || index > lastVisibileElementIndex" class="accent lighten-3 result placeholder">
|
|
|
|
+ <div class="accent lighten-2 image"/>
|
|
<div v-if="photo.Quality < 3 && context === 'review'" style="width: 100%; height: 34px"/>
|
|
<div v-if="photo.Quality < 3 && context === 'review'" style="width: 100%; height: 34px"/>
|
|
- <div class="v-card__title pa-3 card-details v-card__title--primary">
|
|
|
|
|
|
+ <div class="pa-3 card-details">
|
|
<div>
|
|
<div>
|
|
<h3 class="body-2 mb-2" :title="photo.Title">
|
|
<h3 class="body-2 mb-2" :title="photo.Title">
|
|
{{ photo.Title | truncate(80) }}
|
|
{{ photo.Title | truncate(80) }}
|
|
</h3>
|
|
</h3>
|
|
- <div v-if="photo.Description" class="caption mb-2" style="hyphens: auto; word-break: break-word">
|
|
|
|
|
|
+ <div v-if="photo.Description" class="caption mb-2">
|
|
{{ photo.Description }}
|
|
{{ photo.Description }}
|
|
</div>
|
|
</div>
|
|
- <div class="caption" style="hyphens: auto; word-break: break-word">
|
|
|
|
- <i style="display: inline-block; width: 14px" />
|
|
|
|
|
|
+ <div class="caption">
|
|
|
|
+ <i/>
|
|
{{ photo.getDateString(true) }}
|
|
{{ photo.getDateString(true) }}
|
|
<br>
|
|
<br>
|
|
- <i style="display: inline-block; width: 14px" />
|
|
|
|
|
|
+ <i/>
|
|
<template v-if="photo.Type === 'video' || photo.Type === 'animated'">
|
|
<template v-if="photo.Type === 'video' || photo.Type === 'animated'">
|
|
{{ photo.getVideoInfo() }}
|
|
{{ photo.getVideoInfo() }}
|
|
</template>
|
|
</template>
|
|
@@ -57,130 +52,125 @@
|
|
</template>
|
|
</template>
|
|
<template v-if="filter.order === 'name' && $config.feature('download')">
|
|
<template v-if="filter.order === 'name' && $config.feature('download')">
|
|
<br>
|
|
<br>
|
|
- <i style="display: inline-block; width: 14px" />
|
|
|
|
|
|
+ <i/>
|
|
{{ photo.baseName() }}
|
|
{{ photo.baseName() }}
|
|
</template>
|
|
</template>
|
|
<template v-if="featPlaces && photo.Country !== 'zz'">
|
|
<template v-if="featPlaces && photo.Country !== 'zz'">
|
|
<br>
|
|
<br>
|
|
- <i style="display: inline-block; width: 14px" />
|
|
|
|
|
|
+ <i/>
|
|
{{ photo.locationInfo() }}
|
|
{{ photo.locationInfo() }}
|
|
</template>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
- <v-card v-if="index >= firstVisibleElementIndex && index <= lastVisibileElementIndex"
|
|
|
|
- tile
|
|
|
|
- :data-id="photo.ID"
|
|
|
|
- :data-uid="photo.UID"
|
|
|
|
- style="user-select: none"
|
|
|
|
- class="result accent lighten-3"
|
|
|
|
- :class="photo.classes()"
|
|
|
|
- @contextmenu.stop="onContextMenu($event, index)">
|
|
|
|
|
|
+ <div v-else
|
|
|
|
+ tile
|
|
|
|
+ :data-id="photo.ID"
|
|
|
|
+ :data-uid="photo.UID"
|
|
|
|
+ class="result accent lighten-3"
|
|
|
|
+ :class="photo.classes()"
|
|
|
|
+ @contextmenu.stop="onContextMenu($event, index)">
|
|
<div class="card-background accent lighten-3"></div>
|
|
<div class="card-background accent lighten-3"></div>
|
|
- <v-img :key="photo.Hash"
|
|
|
|
- :src="photo.thumbnailUrl('tile_500')"
|
|
|
|
- :alt="photo.Title"
|
|
|
|
- :title="photo.Title"
|
|
|
|
- :transition="false"
|
|
|
|
- aspect-ratio="1"
|
|
|
|
- class="accent lighten-2 clickable"
|
|
|
|
- @touchstart.passive="input.touchStart($event, index)"
|
|
|
|
- @touchend.stop.prevent="onClick($event, index)"
|
|
|
|
- @mousedown.stop.prevent="input.mouseDown($event, index)"
|
|
|
|
- @click.stop.prevent="onClick($event, index)"
|
|
|
|
- @mouseover="playLive(photo)"
|
|
|
|
- @mouseleave="pauseLive(photo)"
|
|
|
|
|
|
+ <div :key="photo.Hash"
|
|
|
|
+ :alt="photo.Title"
|
|
|
|
+ :title="photo.Title"
|
|
|
|
+ class="accent lighten-2 clickable image"
|
|
|
|
+ :style="`background-image: url(${photo.thumbnailUrl('tile_500')})`"
|
|
|
|
+ @touchstart.passive="input.touchStart($event, index)"
|
|
|
|
+ @touchend.stop.prevent="onClick($event, index)"
|
|
|
|
+ @mousedown.stop.prevent="input.mouseDown($event, index)"
|
|
|
|
+ @click.stop.prevent="onClick($event, index)"
|
|
|
|
+ @mouseover="playLive(photo)"
|
|
|
|
+ @mouseleave="pauseLive(photo)"
|
|
>
|
|
>
|
|
<v-layout v-if="photo.Type === 'live' || photo.Type === 'animated'" class="live-player">
|
|
<v-layout v-if="photo.Type === 'live' || photo.Type === 'animated'" class="live-player">
|
|
<video :id="'live-player-' + photo.ID" :key="photo.ID" width="500" height="500" preload="none"
|
|
<video :id="'live-player-' + photo.ID" :key="photo.ID" width="500" height="500" preload="none"
|
|
- loop muted playsinline>
|
|
|
|
|
|
+ loop muted playsinline>
|
|
<source :src="photo.videoUrl()">
|
|
<source :src="photo.videoUrl()">
|
|
</video>
|
|
</video>
|
|
</v-layout>
|
|
</v-layout>
|
|
|
|
|
|
- <v-btn :ripple="false" :depressed="false" class="input-open"
|
|
|
|
- icon flat absolute
|
|
|
|
- @touchstart.stop.prevent="input.touchStart($event, index)"
|
|
|
|
- @touchend.stop.prevent="onOpen($event, index, true)"
|
|
|
|
- @touchmove.stop.prevent
|
|
|
|
- @click.stop.prevent="onOpen($event, index, true)">
|
|
|
|
- <v-icon color="white" class="default-hidden action-raw" :title="$gettext('RAW')">photo_camera</v-icon>
|
|
|
|
- <v-icon color="white" class="default-hidden action-live" :title="$gettext('Live')">$vuetify.icons.live_photo</v-icon>
|
|
|
|
- <v-icon color="white" class="default-hidden action-animated" :title="$gettext('Animated')">gif</v-icon>
|
|
|
|
- <v-icon color="white" class="default-hidden action-play" :title="$gettext('Video')">play_arrow</v-icon>
|
|
|
|
- <v-icon color="white" class="default-hidden action-stack" :title="$gettext('Stack')">burst_mode</v-icon>
|
|
|
|
- </v-btn>
|
|
|
|
|
|
+ <button v-if="photo.Type !== 'image' || photo.Files.length > 1"
|
|
|
|
+ class="input-open"
|
|
|
|
+ @touchstart.stop.prevent="input.touchStart($event, index)"
|
|
|
|
+ @touchend.stop.prevent="onOpen($event, index, true)"
|
|
|
|
+ @touchmove.stop.prevent
|
|
|
|
+ @click.stop.prevent="onOpen($event, index, true)">
|
|
|
|
+ <i v-if="photo.Type === 'raw'" class="action-raw" :title="$gettext('RAW')">photo_camera</i>
|
|
|
|
+ <i v-if="photo.Type === 'live'" class="action-live" :title="$gettext('Live')"><icon-live-photo/></i>
|
|
|
|
+ <i v-if="photo.Type === 'animated'" class="action-animated" :title="$gettext('Animated')">gif</i>
|
|
|
|
+ <i v-if="photo.Type === 'video'" class="action-play" :title="$gettext('Video')">play_arrow</i>
|
|
|
|
+ <i v-if="photo.Type === 'image'" class="action-stack" :title="$gettext('Stack')">burst_mode</i>
|
|
|
|
+ </button>
|
|
|
|
|
|
- <v-btn :ripple="false" :depressed="false" class="input-view"
|
|
|
|
- icon flat absolute :title="$gettext('View')"
|
|
|
|
- @touchstart.stop.prevent="input.touchStart($event, index)"
|
|
|
|
- @touchend.stop.prevent="onOpen($event, index, false)"
|
|
|
|
- @touchmove.stop.prevent
|
|
|
|
- @click.stop.prevent="onOpen($event, index, false)">
|
|
|
|
- <v-icon color="white" class="action-fullscreen">zoom_in</v-icon>
|
|
|
|
- </v-btn>
|
|
|
|
|
|
+ <button v-if="photo.Type === 'image' && selectMode"
|
|
|
|
+ class="input-view"
|
|
|
|
+ :title="$gettext('View')"
|
|
|
|
+ @touchstart.stop.prevent="input.touchStart($event, index)"
|
|
|
|
+ @touchend.stop.prevent="onOpen($event, index, false)"
|
|
|
|
+ @touchmove.stop.prevent
|
|
|
|
+ @click.stop.prevent="onOpen($event, index, false)">
|
|
|
|
+ <i class="action-fullscreen">zoom_in</i>
|
|
|
|
+ </button>
|
|
|
|
|
|
- <v-btn :ripple="false" :depressed="false" color="white" class="input-play"
|
|
|
|
- outline fab large absolute :title="$gettext('Play')"
|
|
|
|
- @touchstart.stop.prevent="input.touchStart($event, index)"
|
|
|
|
- @touchend.stop.prevent="onOpen($event, index, true)"
|
|
|
|
- @touchmove.stop.prevent
|
|
|
|
- @click.stop.prevent="onOpen($event, index, true)">
|
|
|
|
- <v-icon color="white" class="action-play">play_arrow</v-icon>
|
|
|
|
- </v-btn>
|
|
|
|
|
|
+ <button v-if="featPrivate && photo.Private" class="input-private">
|
|
|
|
+ <i class="select-on">lock</i>
|
|
|
|
+ </button>
|
|
|
|
|
|
- <v-btn v-if="featPrivate" :ripple="false"
|
|
|
|
- icon flat absolute
|
|
|
|
- class="input-private">
|
|
|
|
- <v-icon color="white" class="select-on">lock</v-icon>
|
|
|
|
- </v-btn>
|
|
|
|
|
|
+ <!--
|
|
|
|
+ We'd usually use v-if here to only render the button if needed.
|
|
|
|
+ Because the button is supposed to be visible when the result is
|
|
|
|
+ being hovered over, implementing the v-if would require the use of
|
|
|
|
+ a <v-hover> element around the result.
|
|
|
|
|
|
- <v-btn :ripple="false"
|
|
|
|
- icon flat absolute
|
|
|
|
- class="input-select"
|
|
|
|
- @touchstart.stop.prevent="input.touchStart($event, index)"
|
|
|
|
- @touchend.stop.prevent="onSelect($event, index)"
|
|
|
|
- @touchmove.stop.prevent
|
|
|
|
- @click.stop.prevent="onSelect($event, index)">
|
|
|
|
- <v-icon color="white" class="select-on">check_circle</v-icon>
|
|
|
|
- <v-icon color="white" class="select-off">radio_button_off</v-icon>
|
|
|
|
- </v-btn>
|
|
|
|
|
|
+ Because rendering the plain HTML-Button is faster than rendering
|
|
|
|
+ the v-hover component we instead hide the button by default and
|
|
|
|
+ use css to show it when it is being hovered.
|
|
|
|
+ -->
|
|
|
|
+ <button
|
|
|
|
+ class="input-select"
|
|
|
|
+ @touchstart.stop.prevent="input.touchStart($event, index)"
|
|
|
|
+ @touchend.stop.prevent="onSelect($event, index)"
|
|
|
|
+ @touchmove.stop.prevent
|
|
|
|
+ @click.stop.prevent="onSelect($event, index)">
|
|
|
|
+ <i class="select-on">check_circle</i>
|
|
|
|
+ <i class="select-off">radio_button_off</i>
|
|
|
|
+ </button>
|
|
|
|
|
|
- <v-btn :ripple="false"
|
|
|
|
- icon flat absolute
|
|
|
|
- class="input-favorite"
|
|
|
|
- @touchstart.stop.prevent="input.touchStart($event, index)"
|
|
|
|
- @touchend.stop.prevent="toggleLike($event, index)"
|
|
|
|
- @touchmove.stop.prevent
|
|
|
|
- @click.stop.prevent="toggleLike($event, index)">
|
|
|
|
- <v-icon color="white" class="select-on">favorite</v-icon>
|
|
|
|
- <v-icon color="white" class="select-off">favorite_border</v-icon>
|
|
|
|
- </v-btn>
|
|
|
|
- </v-img>
|
|
|
|
|
|
+ <button
|
|
|
|
+ class="input-favorite"
|
|
|
|
+ @touchstart.stop.prevent="input.touchStart($event, index)"
|
|
|
|
+ @touchend.stop.prevent="toggleLike($event, index)"
|
|
|
|
+ @touchmove.stop.prevent
|
|
|
|
+ @click.stop.prevent="toggleLike($event, index)">
|
|
|
|
+ <i v-if="photo.Favorite">favorite</i>
|
|
|
|
+ <i v-else>favorite_border</i>
|
|
|
|
+ </button>
|
|
|
|
+ </div>
|
|
|
|
|
|
<v-card-actions v-if="photo.Quality < 3 && context === 'review'" class="card-details pa-0">
|
|
<v-card-actions v-if="photo.Quality < 3 && context === 'review'" class="card-details pa-0">
|
|
<v-layout row wrap align-center>
|
|
<v-layout row wrap align-center>
|
|
<v-flex xs6 class="text-xs-center pa-1">
|
|
<v-flex xs6 class="text-xs-center pa-1">
|
|
<v-btn color="accent lighten-2"
|
|
<v-btn color="accent lighten-2"
|
|
- small depressed dark block :round="false"
|
|
|
|
- class="action-archive text-xs-center"
|
|
|
|
- :title="$gettext('Archive')" @click.stop="photo.archive()">
|
|
|
|
|
|
+ small depressed dark block :round="false"
|
|
|
|
+ class="action-archive text-xs-center"
|
|
|
|
+ :title="$gettext('Archive')" @click.stop="photo.archive()">
|
|
<v-icon dark>clear</v-icon>
|
|
<v-icon dark>clear</v-icon>
|
|
</v-btn>
|
|
</v-btn>
|
|
</v-flex>
|
|
</v-flex>
|
|
<v-flex xs6 class="text-xs-center pa-1">
|
|
<v-flex xs6 class="text-xs-center pa-1">
|
|
<v-btn color="accent lighten-2"
|
|
<v-btn color="accent lighten-2"
|
|
- small depressed dark block :round="false"
|
|
|
|
- class="action-approve text-xs-center"
|
|
|
|
- :title="$gettext('Approve')" @click.stop="photo.approve()">
|
|
|
|
|
|
+ small depressed dark block :round="false"
|
|
|
|
+ class="action-approve text-xs-center"
|
|
|
|
+ :title="$gettext('Approve')" @click.stop="photo.approve()">
|
|
<v-icon dark>check</v-icon>
|
|
<v-icon dark>check</v-icon>
|
|
</v-btn>
|
|
</v-btn>
|
|
</v-flex>
|
|
</v-flex>
|
|
</v-layout>
|
|
</v-layout>
|
|
</v-card-actions>
|
|
</v-card-actions>
|
|
|
|
|
|
- <v-card-title primary-title class="pa-3 card-details" style="user-select: none;">
|
|
|
|
|
|
+ <div class="pa-3 card-details">
|
|
<div>
|
|
<div>
|
|
<h3 class="body-2 mb-2" :title="photo.Title">
|
|
<h3 class="body-2 mb-2" :title="photo.Title">
|
|
<button class="action-title-edit" :data-uid="photo.UID"
|
|
<button class="action-title-edit" :data-uid="photo.UID"
|
|
@@ -196,30 +186,30 @@
|
|
<div class="caption">
|
|
<div class="caption">
|
|
<button class="action-date-edit" :data-uid="photo.UID"
|
|
<button class="action-date-edit" :data-uid="photo.UID"
|
|
@click.exact="editPhoto(index)">
|
|
@click.exact="editPhoto(index)">
|
|
- <v-icon size="14" :title="$gettext('Taken')">date_range</v-icon>
|
|
|
|
|
|
+ <i :title="$gettext('Taken')">date_range</i>
|
|
{{ photo.getDateString(true) }}
|
|
{{ photo.getDateString(true) }}
|
|
</button>
|
|
</button>
|
|
<br>
|
|
<br>
|
|
<button v-if="photo.Type === 'video'" :title="$gettext('Video')"
|
|
<button v-if="photo.Type === 'video'" :title="$gettext('Video')"
|
|
@click.exact="openPhoto(index, true)">
|
|
@click.exact="openPhoto(index, true)">
|
|
- <v-icon size="14">movie</v-icon>
|
|
|
|
|
|
+ <i>movie</i>
|
|
{{ photo.getVideoInfo() }}
|
|
{{ photo.getVideoInfo() }}
|
|
</button>
|
|
</button>
|
|
<button v-else-if="photo.Type === 'animated'" :title="$gettext('Animated')+' GIF'"
|
|
<button v-else-if="photo.Type === 'animated'" :title="$gettext('Animated')+' GIF'"
|
|
@click.exact="openPhoto(index, true)">
|
|
@click.exact="openPhoto(index, true)">
|
|
- <v-icon size="14">gif_box</v-icon>
|
|
|
|
|
|
+ <i>gif_box</i>
|
|
{{ photo.getVideoInfo() }}
|
|
{{ photo.getVideoInfo() }}
|
|
</button>
|
|
</button>
|
|
<button v-else :title="$gettext('Camera')" class="action-camera-edit"
|
|
<button v-else :title="$gettext('Camera')" class="action-camera-edit"
|
|
:data-uid="photo.UID" @click.exact="editPhoto(index)">
|
|
:data-uid="photo.UID" @click.exact="editPhoto(index)">
|
|
- <v-icon size="14">photo_camera</v-icon>
|
|
|
|
|
|
+ <i>photo_camera</i>
|
|
{{ photo.getPhotoInfo() }}
|
|
{{ photo.getPhotoInfo() }}
|
|
</button>
|
|
</button>
|
|
<template v-if="filter.order === 'name' && $config.feature('download')">
|
|
<template v-if="filter.order === 'name' && $config.feature('download')">
|
|
<br>
|
|
<br>
|
|
<button :title="$gettext('Name')"
|
|
<button :title="$gettext('Name')"
|
|
@click.exact="downloadFile(index)">
|
|
@click.exact="downloadFile(index)">
|
|
- <v-icon size="14">insert_drive_file</v-icon>
|
|
|
|
|
|
+ <i>insert_drive_file</i>
|
|
{{ photo.baseName() }}
|
|
{{ photo.baseName() }}
|
|
</button>
|
|
</button>
|
|
</template>
|
|
</template>
|
|
@@ -227,15 +217,15 @@
|
|
<br>
|
|
<br>
|
|
<button :title="$gettext('Location')" class="action-location"
|
|
<button :title="$gettext('Location')" class="action-location"
|
|
:data-uid="photo.UID" @click.exact="openLocation(index)">
|
|
:data-uid="photo.UID" @click.exact="openLocation(index)">
|
|
- <v-icon size="14">location_on</v-icon>
|
|
|
|
|
|
+ <i>location_on</i>
|
|
{{ photo.locationInfo() }}
|
|
{{ photo.locationInfo() }}
|
|
</button>
|
|
</button>
|
|
</template>
|
|
</template>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
- </v-card-title>
|
|
|
|
- </v-card>
|
|
|
|
- </v-flex>
|
|
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
|
|
+ </div>
|
|
</v-layout>
|
|
</v-layout>
|
|
</v-container>
|
|
</v-container>
|
|
</template>
|
|
</template>
|
|
@@ -244,9 +234,13 @@ import download from "common/download";
|
|
import Notify from "common/notify";
|
|
import Notify from "common/notify";
|
|
import {Input, InputInvalid, ClickShort, ClickLong} from "common/input";
|
|
import {Input, InputInvalid, ClickShort, ClickLong} from "common/input";
|
|
import {virtualizationTools} from 'common/virtualization-tools';
|
|
import {virtualizationTools} from 'common/virtualization-tools';
|
|
|
|
+import IconLivePhoto from "component/icon/live_photo.vue";
|
|
|
|
|
|
export default {
|
|
export default {
|
|
name: 'PPhotoCards',
|
|
name: 'PPhotoCards',
|
|
|
|
+ components: {
|
|
|
|
+ IconLivePhoto,
|
|
|
|
+ },
|
|
props: {
|
|
props: {
|
|
photos: {
|
|
photos: {
|
|
type: Array,
|
|
type: Array,
|
|
@@ -321,8 +315,16 @@ export default {
|
|
if (this.$refs.items === undefined) {
|
|
if (this.$refs.items === undefined) {
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
- for (const item of this.$refs.items) {
|
|
|
|
- this.intersectionObserver.observe(item);
|
|
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * observing only every 5th item reduces the amount of time
|
|
|
|
+ * spent computing intersection by 80%. me might render up to
|
|
|
|
+ * 8 items more than required, but the time saved computing
|
|
|
|
+ * intersections is far greater than the time lost rendering
|
|
|
|
+ * a couple more items
|
|
|
|
+ */
|
|
|
|
+ for (let i = 0; i < this.$refs.items.length; i += 5) {
|
|
|
|
+ this.intersectionObserver.observe(this.$refs.items[i]);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
elementIndexFromIntersectionObserverEntry(entry) {
|
|
elementIndexFromIntersectionObserverEntry(entry) {
|
|
@@ -335,8 +337,10 @@ export default {
|
|
this.elementIndexFromIntersectionObserverEntry,
|
|
this.elementIndexFromIntersectionObserverEntry,
|
|
);
|
|
);
|
|
|
|
|
|
- this.firstVisibleElementIndex = smallestIndex;
|
|
|
|
- this.lastVisibileElementIndex = largestIndex;
|
|
|
|
|
|
+ // we observe only every 5th item, so we increase the rendered
|
|
|
|
+ // range here by 4 items in every directio just to be safe
|
|
|
|
+ this.firstVisibleElementIndex = smallestIndex - 4;
|
|
|
|
+ this.lastVisibileElementIndex = largestIndex + 4;
|
|
},
|
|
},
|
|
livePlayer(photo) {
|
|
livePlayer(photo) {
|
|
return document.querySelector("#live-player-" + photo.ID);
|
|
return document.querySelector("#live-player-" + photo.ID);
|
|
@@ -388,9 +392,19 @@ export default {
|
|
if (ev.shiftKey) {
|
|
if (ev.shiftKey) {
|
|
this.selectRange(index);
|
|
this.selectRange(index);
|
|
} else {
|
|
} else {
|
|
- this.$clipboard.toggle(this.photos[index]);
|
|
|
|
|
|
+ this.toggle(this.photos[index]);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
|
|
+ toggle(photo) {
|
|
|
|
+ this.$clipboard.toggle(photo);
|
|
|
|
+ /**
|
|
|
|
+ * updating the clipboard does not rerender this component. Because of that
|
|
|
|
+ * there can be scenarios where the select-icon is missing after a change,
|
|
|
|
+ * for example when using touch and no hover-state changes.We therefore
|
|
|
|
+ * force an update to fix that.
|
|
|
|
+ */
|
|
|
|
+ this.$forceUpdate();
|
|
|
|
+ },
|
|
onOpen(ev, index, showMerged) {
|
|
onOpen(ev, index, showMerged) {
|
|
const inputType = this.input.eval(ev, index);
|
|
const inputType = this.input.eval(ev, index);
|
|
|
|
|
|
@@ -412,7 +426,7 @@ export default {
|
|
if (longClick || ev.shiftKey) {
|
|
if (longClick || ev.shiftKey) {
|
|
this.selectRange(index);
|
|
this.selectRange(index);
|
|
} else {
|
|
} else {
|
|
- this.$clipboard.toggle(this.photos[index]);
|
|
|
|
|
|
+ this.toggle(this.photos[index]);
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
this.openPhoto(index, false);
|
|
this.openPhoto(index, false);
|
|
@@ -427,6 +441,13 @@ export default {
|
|
},
|
|
},
|
|
selectRange(index) {
|
|
selectRange(index) {
|
|
this.$clipboard.addRange(index, this.photos);
|
|
this.$clipboard.addRange(index, this.photos);
|
|
|
|
+ /**
|
|
|
|
+ * updating the clipboard does not rerender this component. Because of that
|
|
|
|
+ * there can be scenarios where the select-icon is missing after a change,
|
|
|
|
+ * for example when selecting mutliple elements at once. We therefore
|
|
|
|
+ * force an update to fix that.
|
|
|
|
+ */
|
|
|
|
+ this.$forceUpdate();
|
|
},
|
|
},
|
|
}
|
|
}
|
|
};
|
|
};
|