Uprava editoru

This commit is contained in:
Michal Kváček
2023-10-31 17:29:51 +01:00
parent 1ed35552d5
commit 3e7ee9e686
7 changed files with 141 additions and 466 deletions
-80
View File
@@ -1,80 +0,0 @@
<script setup>
import PhotoDetail from "components/photos/PhotoDetail.vue";
import {onMounted, ref, watch} from "vue";
import {useRoute, useRouter} from "vue-router";
const props = defineProps({
photos: {
type: Array,
required: true
},
coverPhotoUrl: {
type: String,
required: false
},
carouselTitle: {
type: String,
required: false
},
defaultTitle: {
type: String,
required: false
}
})
const $route = useRoute();
const photoId = parseInt($route.query.photo);
const displayedPhoto = ref(null);
const emits = defineEmits(["open-detail"]);
watch(() => props.photos, (newVal) => {
if (newVal.length === 0) return;
displayedPhoto.value = props.coverPhotoUrl || newVal[0].url;
}, {immediate: true});
const openDetail = ({photo, idx, allPhotos}) => {
emits("open-detail", {photo, idx, allPhotos});
}
onMounted(() => {
const idx = props.photos.findIndex(p => p.id === photoId);
if (idx >= 0) {
const photo = props.photos[idx];
openDetail({photo, allPhotos: props.photos, idx});
}
});
</script>
<template>
<q-carousel
v-if="photos.length > 0"
animated
v-model="displayedPhoto"
arrows
swipeable
height="auto"
infinite
v-bind="$attrs"
>
<q-carousel-slide style="padding: 0" v-for="(photo, idx) in photos" :key="photo.url" :name="photo.url">
<q-img :src="photo.url">
<span class="absolute-top text-right">
<q-btn
round
icon="zoom_in"
class="bg-blue-grey-9 text-white q-ma-md all-pointer-events"
@click="openDetail({photo, idx, allPhotos: photos})" />
</span>
<div class="absolute-bottom custom-caption">
<div class="text-h5" v-if="carouselTitle"> {{ carouselTitle }} </div>
<div class="text-subtitle1" v-if="photos.length > 0">
<q-btn rounded @click.prevent="openDetail({photo: photos[0], idx: 0, allPhotos: photos})">{{photos.length}} fotografií</q-btn>
</div>
</div>
</q-img>
</q-carousel-slide>
</q-carousel>
</template>
-261
View File
@@ -1,261 +0,0 @@
<script setup>
import EmbeddedMap from "components/map/EmbeddedMap.vue";
import {computed, ref, watch} from "vue";
import {formatDatetime} from "src/composables/datetimeUtils";
import 'vue-inner-image-zoom/lib/vue-inner-image-zoom.css'
import {useQuasar} from "quasar";
import {useRouter} from "vue-router";
const props = defineProps({
photos: {
type: Array,
required: false,
default: () => []
},
openedIndex: {
type: Number,
required: false,
},
modelValue: {
type: Boolean,
required: false,
default: false
},
defaultTitle: {
type: String,
required: false
}
});
const $q = useQuasar();
const $router = useRouter();
const imageFullscreen = ref(false);
const isMaximized = computed(() => $q.screen.width <= $q.screen.sizes.sm);
const displayedPhotoIndex = ref(null);
watch(() => props.openedIndex, (newVal) => {
displayedPhotoIndex.value = newVal;
}, {immediate: true});
const photo = computed(() => props.photos[displayedPhotoIndex.value]);
const emit = defineEmits(["update:modelValue"])
const isDetailOpened = computed({
get() {
return props.modelValue;
},
set(newVal) {
emit("update:modelValue", newVal);
}
});
const nextPhoto = () => {
if (imageFullscreen.value) return;
if (displayedPhotoIndex.value < props.photos.length - 1) {
displayedPhotoIndex.value++;
scrollToPhoto();
}
}
const prevPhoto = () => {
if (imageFullscreen.value) return;
if (displayedPhotoIndex.value > 0) {
displayedPhotoIndex.value--;
scrollToPhoto();
}
}
const openDetail = (idx) => {
displayedPhotoIndex.value = idx;
scrollToPhoto();
};
watch(() => displayedPhotoIndex.value, (newVal) => {
const photo = props.photos[newVal];
$router.replace({query: {photo: photo.id}});
}, {immediate: true});
const removePhotoFromQueries = () => {
$router.replace({query: {photo: undefined}});
}
const showExtra = ref(false);
const scrollArea = ref(null);
const infoWrapper = ref(null);
const scrollToPhoto = () => {
scrollArea.value.setScrollPosition('vertical', 0, 500);
}
const showExtraInfo = () => {
if (showExtra.value) {
showExtra.value = false;
scrollToPhoto();
} else {
showExtra.value = true;
const scrollAreaHeight = infoWrapper.value.offsetTop;
scrollArea.value.setScrollPosition('vertical', scrollAreaHeight, 500);
}
}
</script>
<template>
<q-dialog
v-model="isDetailOpened"
full-width
:maximized="isMaximized"
no-route-dismiss
@hide="removePhotoFromQueries"
>
<q-card>
<q-scroll-area ref="scrollArea" style="height: 95vh">
<q-card-section class="bg-black text-white" v-if="isMaximized">
<div class="row">
<div class="col">
<strong>{{ photo.name || defaultTitle }}</strong>
</div>
<div class="col-1 text-right">
<q-btn round text-color="white" size="sm" icon="close" @click="isDetailOpened = false"/>
</div>
</div>
</q-card-section>
<div class="fixed-top-right q-mt-md q-mr-md z-max" v-else>
<q-btn round size="sm" icon="close" @click="isDetailOpened = false"/>
</div>
<q-card-section :class="{'q-pa-none': isMaximized}">
<div :class="{'relative-position': true, 'image-full-height': imageFullscreen}">
<div
class="text-center"
v-touch-swipe.mouse.right="prevPhoto"
v-touch-swipe.mouse.left="nextPhoto"
@keyup.left="prevPhoto"
@keyup.right="nextPhoto">
<inner-image-zoom
:move-type="isMaximized ? 'drag' : 'pan'"
:zoomScale="0.75"
:hide-hint="true"
:after-zoom-in="() => imageFullscreen = true"
:after-zoom-out="() => imageFullscreen = false"
:src="photo.url"
/>
</div>
<template v-if="!imageFullscreen">
<q-btn
class="absolute all-pointer-events"
round
flat
icon="info"
:text-color="showExtra ? 'primary' : 'white'"
style="background: rgba(0, 0, 0, 0.25); bottom: 16px; left: 8px"
@click="showExtraInfo"
/>
<q-btn
class="absolute all-pointer-events"
style="background: rgba(0, 0, 0, 0.25); top: 50%; left: 8px;"
@click="prevPhoto"
v-if="displayedPhotoIndex > 0 && !isMaximized"
icon="chevron_left"
text-color="white"
flat
rounded
/>
<q-btn
class="absolute all-pointer-events"
style="background: rgba(0, 0, 0, 0.25); top: 50%; right: 8px;"
@click="nextPhoto"
v-if="displayedPhotoIndex < photos.length - 1 && !isMaximized"
icon-right="chevron_right"
text-color="white"
flat
rounded
/>
</template>
</div>
<div class="row" v-show="!imageFullscreen">
<div class="col-3 col-md-1 q-pa-sm" v-for="(thumb, idx) in photos" :key="thumb.thumbnailUrl">
<q-img
:src="thumb.thumbnailUrl"
@click="openDetail(idx)"
:class='{"shadow-5": true, "active-thumbnail": idx === displayedPhotoIndex}'
/>
</div>
</div>
<div class="row" ref="infoWrapper">
<div class="col-12 q-my-md" v-if="showExtra">
<EmbeddedMap
v-if="photo.gpsLatitude && photo.gpsLongitude"
class="q-mx-sm"
:gps-latitude="photo.gpsLatitude"
:gps-longitude="photo.gpsLongitude"
/>
<strong v-if="photo.name !== '' && !isMaximized">{{ photo.name }}</strong>
<p v-if="photo.description !== ''">{{ photo.description }}</p>
<q-list dense>
<q-item v-if="photo.pointOfInterest">
<q-item-section avatar>
<q-avatar icon="marker"></q-avatar>
</q-item-section>
<q-item-section>
{{ photo.pointOfInterest.name }}
</q-item-section>
</q-item>
<q-item v-if="photo.exposedAt">
<q-item-section avatar>
<q-avatar icon="photo_camera"></q-avatar>
</q-item-section>
<q-item-section>
{{ formatDatetime(photo.exposedAt) }}
</q-item-section>
</q-item>
<q-item v-if="photo.gpsAltitude">
<q-item-section avatar>
<q-avatar icon="height"/>
</q-item-section>
<q-item-section>
Nadmořská výška: {{ photo.gpsAltitude }} m
<br>
<template v-if="photo.terrainElevation">
Výška terénu: {{ photo.terrainElevation }} m
<br>
Výška nad zemí: {{ photo.gpsAltitude - photo.terrainElevation }} m
</template>
</q-item-section>
</q-item>
</q-list>
</div>
</div>
</q-card-section>
</q-scroll-area>
</q-card>
</q-dialog>
</template>
<style>
.active-thumbnail {
outline: solid 2px
}
.iiz {
width: 100%;
}
.image-full-height .iiz {
height: calc(95vh - 65px) !important;
}
.iiz__img {
max-height: 90vh !important;
margin: 0 auto;
}
.iiz__img--hidden {
height: 100vh;
}
</style>
+3 -3
View File
@@ -111,7 +111,7 @@ const batchOptionsProps = computed(() => ({
</script>
<template>
<div class="row q-mb-lg">
<div class="row" style="margin-bottom: 150px">
<div class="col-6 col-sm-4 col-md-4 col-lg-3" v-for="(photo, idx) in photos" :key="photo.id">
<q-card :class='{"q-ma-sm": true, "shadow-1": !batchEdit[photo.id], "shadow-10": batchEdit[photo.id]}'>
<q-img :src="photo.thumbnailUrl" @click="handleImageClick(photo, idx)" style="cursor: pointer">
@@ -143,8 +143,8 @@ const batchOptionsProps = computed(() => ({
<q-card-actions>
<q-checkbox v-model="checkAll" label="Vybrat vše"/>
<q-btn v-bind="batchOptionsProps" icon="info" :color="isBatchEditAllowed ? 'primary' : 'grey'" @click="openBatchEdit"/>
<q-btn v-bind="batchOptionsProps" icon="rotate_left" :color="isBatchEditAllowed ? 'secondary' : 'grey'" @click="batchRotate('counterClockwise')"/>
<q-btn v-bind="batchOptionsProps" icon="rotate_right" :color="isBatchEditAllowed ? 'secondary' : 'grey'" @click="batchRotate('clockwise')"/>
<q-btn v-bind="batchOptionsProps" icon="rotate_90_degrees_ccw" :color="isBatchEditAllowed ? 'secondary' : 'grey'" @click="batchRotate('counterClockwise')"/>
<q-btn v-bind="batchOptionsProps" icon="rotate_90_degrees_cw" :color="isBatchEditAllowed ? 'secondary' : 'grey'" @click="batchRotate('clockwise')"/>
<q-btn v-bind="batchOptionsProps" icon="delete" :color="isBatchEditAllowed ? 'red' : 'grey'" @click="confirmBatchPhotoDelete"/>
</q-card-actions>
</q-card>