mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-12 23:35:58 +00:00
file upload: refactored
This commit is contained in:
parent
c304b74f17
commit
650e83f4c2
@ -1,17 +1,21 @@
|
||||
<template>
|
||||
<div class="image-uploader">
|
||||
<vue-dropzone
|
||||
v-show="!showCropper"
|
||||
v-show="!showCropper && !hasImage"
|
||||
id="postdropzone"
|
||||
:options="dropzoneOptions"
|
||||
:use-custom-slot="true"
|
||||
@vdropzone-error="onDropzoneError"
|
||||
@vdropzone-file-added="fileAdded"
|
||||
>
|
||||
<loading-spinner v-if="isLoadingImage" />
|
||||
<base-icon v-else-if="!hasImage" name="image" />
|
||||
<div v-if="!hasImage" class="supported-formats">
|
||||
{{ $t('contribution.teaserImage.supportedFormats') }}
|
||||
</div>
|
||||
</vue-dropzone>
|
||||
<div v-show="!showCropper && hasImage">
|
||||
<base-button
|
||||
v-if="hasImage"
|
||||
class="delete-image-button"
|
||||
icon="trash"
|
||||
circle
|
||||
danger
|
||||
@ -20,10 +24,7 @@
|
||||
:title="$t('actions.delete')"
|
||||
@click.stop="deleteImage"
|
||||
/>
|
||||
<div v-if="!hasImage" class="supported-formats">
|
||||
{{ $t('contribution.teaserImage.supportedFormats') }}
|
||||
</div>
|
||||
</vue-dropzone>
|
||||
</div>
|
||||
<div v-show="!showCropper && imageCanBeCropped" class="crop-overlay">
|
||||
<base-button class="crop-confirm" filled @click="initCropper">
|
||||
{{ $t('contribution.teaserImage.cropImage') }}
|
||||
@ -53,6 +54,8 @@ import Cropper from 'cropperjs'
|
||||
import LoadingSpinner from '~/components/_new/generic/LoadingSpinner/LoadingSpinner'
|
||||
import 'cropperjs/dist/cropper.css'
|
||||
|
||||
const minAspectRatio = 0.3
|
||||
|
||||
export default {
|
||||
components: {
|
||||
LoadingSpinner,
|
||||
@ -80,12 +83,8 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onDropzoneError(file, message) {
|
||||
this.$toast.error(file.status, message)
|
||||
},
|
||||
|
||||
onUnSupportedFormat(status, message) {
|
||||
this.$toast.error(status, message)
|
||||
onUnSupportedFormat(message) {
|
||||
this.$toast.error(message)
|
||||
},
|
||||
addImageProcess(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
@ -96,46 +95,45 @@ export default {
|
||||
})
|
||||
},
|
||||
async fileAdded(file) {
|
||||
const supportedFormats = ['image/jpg', 'image/jpeg', 'image/png', 'image/gif']
|
||||
if (supportedFormats.indexOf(file.type) < 0) {
|
||||
this.onUnSupportedFormat(this.$t('contribution.teaserImage.errors.unSupported-file-format'))
|
||||
this.$nextTick((this.isLoadingImage = false))
|
||||
return null
|
||||
}
|
||||
const imageURL = URL.createObjectURL(file)
|
||||
const image = await this.addImageProcess(imageURL)
|
||||
this.$emit('addImageAspectRatio', image.width / image.height || 1.0)
|
||||
this.$emit('addHeroImage', file)
|
||||
this.$emit('addImageType', file.type)
|
||||
const aspectRatio = image.width / image.height
|
||||
if (aspectRatio < minAspectRatio) {
|
||||
this.aspectRatioError()
|
||||
return null
|
||||
}
|
||||
this.saveImage(aspectRatio, file, file.type)
|
||||
this.file = file
|
||||
if (this.file.type === 'image/jpeg') this.imageCanBeCropped = true
|
||||
this.$nextTick((this.isLoadingImage = false))
|
||||
},
|
||||
initCropper() {
|
||||
const supportedFormats = ['image/jpg', 'image/jpeg', 'image/png', 'image/gif']
|
||||
|
||||
if (supportedFormats.indexOf(this.file.type) < 0) {
|
||||
this.onUnSupportedFormat(
|
||||
'error',
|
||||
this.$t('contribution.teaserImage.errors.unSupported-file-format'),
|
||||
)
|
||||
return
|
||||
}
|
||||
this.showCropper = true
|
||||
|
||||
const imageElement = document.querySelector('#cropping-image')
|
||||
imageElement.src = URL.createObjectURL(this.file)
|
||||
this.cropper = new Cropper(imageElement, { zoomable: false, autoCropArea: 0.9 })
|
||||
},
|
||||
cropImage() {
|
||||
this.isLoadingImage = true
|
||||
|
||||
const onCropComplete = (aspectRatio, imageFile, imageType) => {
|
||||
this.$emit('addImageAspectRatio', aspectRatio)
|
||||
this.$emit('addHeroImage', imageFile)
|
||||
this.$emit('addImageType', imageType)
|
||||
this.saveImage(aspectRatio, imageFile, imageType)
|
||||
this.$nextTick((this.isLoadingImage = false))
|
||||
this.closeCropper()
|
||||
}
|
||||
|
||||
if (this.file.type === 'image/jpeg') {
|
||||
const canvas = this.cropper.getCroppedCanvas()
|
||||
canvas.toBlob((blob) => {
|
||||
const imageAspectRatio = canvas.width / canvas.height
|
||||
if (imageAspectRatio < minAspectRatio) {
|
||||
this.aspectRatioError()
|
||||
return
|
||||
}
|
||||
const croppedImageFile = new File([blob], this.file.name, { type: this.file.type })
|
||||
onCropComplete(imageAspectRatio, croppedImageFile, 'image/jpeg')
|
||||
}, 'image/jpeg')
|
||||
@ -149,6 +147,14 @@ export default {
|
||||
this.showCropper = false
|
||||
this.cropper.destroy()
|
||||
},
|
||||
aspectRatioError() {
|
||||
this.$toast.error(this.$t('contribution.teaserImage.errors.aspect-ratio-too-small'))
|
||||
},
|
||||
saveImage(aspectRatio = 1.0, file, fileType) {
|
||||
this.$emit('addImageAspectRatio', aspectRatio)
|
||||
this.$emit('addHeroImage', file)
|
||||
this.$emit('addImageType', fileType)
|
||||
},
|
||||
deleteImage() {
|
||||
this.$emit('addHeroImage', null)
|
||||
this.$emit('addImageAspectRatio', null)
|
||||
@ -161,7 +167,6 @@ export default {
|
||||
.image-uploader {
|
||||
position: relative;
|
||||
min-height: $size-image-uploader-min-height;
|
||||
cursor: pointer;
|
||||
|
||||
.image + & {
|
||||
position: absolute;
|
||||
@ -206,6 +211,14 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
.delete-image-button {
|
||||
position: absolute;
|
||||
top: $space-small;
|
||||
right: $space-small;
|
||||
z-index: $z-index-surface;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dz-message {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
@ -214,6 +227,7 @@ export default {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: $z-index-surface;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
> .base-icon {
|
||||
|
||||
@ -216,6 +216,7 @@
|
||||
"cropImage": "Bild zuschneiden",
|
||||
"cropperConfirm": "Bestätigen",
|
||||
"errors": {
|
||||
"aspect-ratio-too-small": "Dieses Bild ist zu hoch.",
|
||||
"unSupported-file-format": "Bitte lade ein Bild in den folgenden Formaten hoch: JPG, JPEG, PNG or GIF!"
|
||||
},
|
||||
"supportedFormats": "Füge ein Bild im Dateiformat JPG, PNG oder GIF ein"
|
||||
|
||||
@ -216,6 +216,7 @@
|
||||
"cropImage": "Crop image",
|
||||
"cropperConfirm": "Confirm",
|
||||
"errors": {
|
||||
"aspect-ratio-too-small": "This image is too high.",
|
||||
"unSupported-file-format": "Please upload an image of file format: JPG, JPEG, PNG or GIF!"
|
||||
},
|
||||
"supportedFormats": "Insert a picture of file format JPG, PNG or GIF"
|
||||
|
||||
@ -180,11 +180,9 @@ export default {
|
||||
if (!this.post.image || typeof this.post.image.aspectRatio !== 'number') return false
|
||||
/* Return the aspect ratio as a css variable. Later to be used when calculating
|
||||
the height with respect to the width.
|
||||
Why is this correction necessary?
|
||||
*/
|
||||
return {
|
||||
'--hero-image-aspect-ratio': 1.0 / this.post.image.aspectRatio,
|
||||
'--hero-image-correction': 1.0 / this.post.image.aspectRatio * 48.0 + 'px',
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -261,7 +259,7 @@ export default {
|
||||
the autoscroll works correctly when following a comment link.
|
||||
*/
|
||||
|
||||
padding-top: calc(var(--hero-image-aspect-ratio) * 100% + var(--hero-image-correction));
|
||||
padding-top: calc(var(--hero-image-aspect-ratio) * (100% + 48px));
|
||||
/* Letting the image fill the container, since the container
|
||||
is the one determining height
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user