mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge pull request #1666 from Human-Connection/1466_image_cropping
🍰 Implement basic image cropping solution
This commit is contained in:
commit
63cc82edea
@ -18,7 +18,6 @@ export default async function fileUpload(params, { file, url }, uploadCallback =
|
||||
const fileLocation = `/uploads/${Date.now()}-${slug(name)}`
|
||||
await uploadCallback({ createReadStream, fileLocation })
|
||||
delete params[file]
|
||||
|
||||
params[url] = fileLocation
|
||||
}
|
||||
|
||||
|
||||
@ -262,7 +262,7 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.smallTag {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
||||
@ -131,10 +131,11 @@ export default {
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.ds-card-image img {
|
||||
width: 100%;
|
||||
max-height: 300px;
|
||||
max-height: 2000px;
|
||||
object-fit: contain;
|
||||
-o-object-fit: cover;
|
||||
object-fit: cover;
|
||||
-o-object-position: center;
|
||||
|
||||
@ -25,17 +25,6 @@ describe('TeaserImage.vue', () => {
|
||||
wrapper = Wrapper()
|
||||
})
|
||||
|
||||
describe('File upload', () => {
|
||||
const imageUpload = [
|
||||
{ file: { filename: 'avataar.svg', previewElement: '' }, url: 'someUrlToImage' },
|
||||
]
|
||||
|
||||
it('supports adding a teaser image', () => {
|
||||
wrapper.vm.addTeaserImage(imageUpload)
|
||||
expect(wrapper.emitted().addTeaserImage[0]).toEqual(imageUpload)
|
||||
})
|
||||
})
|
||||
|
||||
describe('handles errors', () => {
|
||||
beforeEach(() => jest.useFakeTimers())
|
||||
const message = 'File upload failed'
|
||||
|
||||
@ -5,25 +5,24 @@
|
||||
id="postdropzone"
|
||||
class="ds-card-image"
|
||||
:use-custom-slot="true"
|
||||
@vdropzone-thumbnail="thumbnail"
|
||||
@vdropzone-error="verror"
|
||||
@vdropzone-thumbnail="transformImage"
|
||||
@vdropzone-drop="dropzoneDrop"
|
||||
>
|
||||
<div class="dz-message">
|
||||
<div
|
||||
:class="{
|
||||
'hc-attachments-upload-area-post': true,
|
||||
'hc-attachments-upload-area-update-post': contribution,
|
||||
}"
|
||||
>
|
||||
<slot></slot>
|
||||
<div
|
||||
:class="{
|
||||
'hc-attachments-upload-area-post': true,
|
||||
'hc-attachments-upload-area-update-post': contribution,
|
||||
'hc-drag-marker-post': true,
|
||||
'hc-drag-marker-update-post': contribution,
|
||||
}"
|
||||
>
|
||||
<slot></slot>
|
||||
<div
|
||||
:class="{
|
||||
'hc-drag-marker-post': true,
|
||||
'hc-drag-marker-update-post': contribution,
|
||||
}"
|
||||
>
|
||||
<ds-icon name="image" size="xxx-large" />
|
||||
</div>
|
||||
<ds-icon name="image" size="xxx-large" />
|
||||
</div>
|
||||
</div>
|
||||
</vue-dropzone>
|
||||
@ -31,6 +30,8 @@
|
||||
|
||||
<script>
|
||||
import vueDropzone from 'nuxt-dropzone'
|
||||
import Cropper from 'cropperjs'
|
||||
import 'cropperjs/dist/cropper.css'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -42,7 +43,7 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
dropzoneOptions: {
|
||||
url: this.addTeaserImage,
|
||||
url: () => '',
|
||||
maxFilesize: 5.0,
|
||||
previewTemplate: this.template(),
|
||||
},
|
||||
@ -70,27 +71,50 @@ export default {
|
||||
this.error = true
|
||||
this.$toast.error(file.status, message)
|
||||
},
|
||||
addTeaserImage(file) {
|
||||
this.$emit('addTeaserImage', file[0])
|
||||
return ''
|
||||
transformImage(file) {
|
||||
let thumbnailElement, editor, confirm, thumbnailPreview, contributionImage
|
||||
// Create the image editor overlay
|
||||
editor = document.createElement('div')
|
||||
thumbnailElement = document.querySelectorAll('#postdropzone')[0]
|
||||
thumbnailPreview = document.querySelectorAll('.thumbnail-preview')[0]
|
||||
if (thumbnailPreview) thumbnailPreview.remove()
|
||||
contributionImage = document.querySelectorAll('.contribution-image')[0]
|
||||
if (contributionImage) contributionImage.remove()
|
||||
editor.classList.add('crop-overlay')
|
||||
thumbnailElement.appendChild(editor)
|
||||
// Create the confirm button
|
||||
confirm = document.createElement('button')
|
||||
confirm.classList.add('crop-confirm', 'ds-button', 'ds-button-primary')
|
||||
confirm.textContent = this.$t('contribution.teaserImage.cropperConfirm')
|
||||
confirm.addEventListener('click', () => {
|
||||
// Get the canvas with image data from Cropper.js
|
||||
let canvas = cropper.getCroppedCanvas()
|
||||
canvas.toBlob(blob => {
|
||||
this.$refs.el.manuallyAddFile(blob, canvas.toDataURL(), null, null, {
|
||||
dontSubstractMaxFiles: false,
|
||||
addToFiles: true,
|
||||
})
|
||||
image = new Image()
|
||||
image.src = canvas.toDataURL()
|
||||
image.classList.add('thumbnail-preview')
|
||||
thumbnailElement.appendChild(image)
|
||||
// Remove the editor from view
|
||||
editor.parentNode.removeChild(editor)
|
||||
this.$emit('addTeaserImage', blob)
|
||||
})
|
||||
})
|
||||
editor.appendChild(confirm)
|
||||
|
||||
// Load the image
|
||||
let image = new Image()
|
||||
image.src = URL.createObjectURL(file)
|
||||
editor.appendChild(image)
|
||||
// Create Cropper.js and pass image
|
||||
let cropper = new Cropper(image, { zoomable: false })
|
||||
},
|
||||
thumbnail: (file, dataUrl) => {
|
||||
let thumbnailElement, contributionImage, uploadArea, thumbnailPreview, image
|
||||
if (file.previewElement) {
|
||||
thumbnailElement = document.querySelectorAll('#postdropzone')[0]
|
||||
contributionImage = document.querySelectorAll('.contribution-image')[0]
|
||||
thumbnailPreview = document.querySelectorAll('.thumbnail-preview')[0]
|
||||
if (contributionImage) {
|
||||
uploadArea = document.querySelectorAll('.hc-attachments-upload-area-update-post')[0]
|
||||
uploadArea.removeChild(contributionImage)
|
||||
uploadArea.classList.remove('hc-attachments-upload-area-update-post')
|
||||
}
|
||||
image = new Image()
|
||||
image.src = URL.createObjectURL(file)
|
||||
image.classList.add('thumbnail-preview')
|
||||
if (thumbnailPreview) return thumbnailElement.replaceChild(image, thumbnailPreview)
|
||||
thumbnailElement.appendChild(image)
|
||||
}
|
||||
dropzoneDrop() {
|
||||
let cropOverlay = document.querySelectorAll('.crop-overlay')[0]
|
||||
if (cropOverlay) cropOverlay.remove()
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -98,16 +122,10 @@ export default {
|
||||
<style lang="scss">
|
||||
#postdropzone {
|
||||
width: 100%;
|
||||
min-height: 300px;
|
||||
min-height: 500px;
|
||||
background-color: $background-color-softest;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 960px) {
|
||||
#postdropzone {
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
.hc-attachments-upload-area-post {
|
||||
position: relative;
|
||||
display: flex;
|
||||
@ -134,11 +152,10 @@ export default {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 180px 5px;
|
||||
color: hsl(0, 0%, 25%);
|
||||
transition: all 0.2s ease-out;
|
||||
font-size: 60px;
|
||||
margin: 80px 5px;
|
||||
|
||||
background-color: $background-color-softest;
|
||||
opacity: 0.65;
|
||||
|
||||
@ -178,7 +195,17 @@ export default {
|
||||
border-top: $border-size-base solid $border-color-softest;
|
||||
}
|
||||
|
||||
.contribution-image {
|
||||
max-height: 300px;
|
||||
.crop-overlay {
|
||||
max-height: 2000px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.crop-confirm {
|
||||
position: absolute;
|
||||
left: 10px;
|
||||
top: 10px;
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -562,6 +562,9 @@
|
||||
"it-internet-data-privacy": "IT, Internet & Datenschutz",
|
||||
"art-culture-sport": "Kunst, Kultur & Sport"
|
||||
}
|
||||
},
|
||||
"teaserImage": {
|
||||
"cropperConfirm": "Bestätigen"
|
||||
}
|
||||
},
|
||||
"code-of-conduct": {
|
||||
|
||||
@ -563,6 +563,9 @@
|
||||
"it-internet-data-privacy": "IT, Internet & Data Privacy",
|
||||
"art-culture-sport": "Art, Culture, & Sport"
|
||||
}
|
||||
},
|
||||
"teaserImage": {
|
||||
"cropperConfirm": "Confirm"
|
||||
}
|
||||
},
|
||||
"code-of-conduct": {
|
||||
|
||||
@ -292,6 +292,11 @@
|
||||
"message": "¿Realmente quieres liberar el comentario de \"<b>{name}</b>\"?"
|
||||
}
|
||||
},
|
||||
"contribution": {
|
||||
"teaserImage": {
|
||||
"cropperConfirm": "Confirmar"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"avatar": {
|
||||
"submitted": "Carga con éxito"
|
||||
|
||||
@ -287,6 +287,11 @@
|
||||
"message": "Voulez-vous vraiment publier le commentaire de \"<b>{name}</b>\"?"
|
||||
}
|
||||
},
|
||||
"contribution": {
|
||||
"teaserImage": {
|
||||
"cropperConfirm": "Confirmer"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"avatar": {
|
||||
"submitted": "Téléchargement réussi"
|
||||
|
||||
@ -140,5 +140,10 @@
|
||||
"save": "Salva",
|
||||
"edit": "Modifica",
|
||||
"delete": "Cancella"
|
||||
},
|
||||
"contribution": {
|
||||
"teaserImage": {
|
||||
"cropperConfirm": "Confermare"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,7 +158,10 @@
|
||||
},
|
||||
"contribution": {
|
||||
"edit": "Bijdrage bewerken",
|
||||
"delete": "Bijdrage verwijderen"
|
||||
"delete": "Bijdrage verwijderen",
|
||||
"teaserImage": {
|
||||
"cropperConfirm": "Bevestigen"
|
||||
}
|
||||
},
|
||||
"comment": {
|
||||
"edit": "Commentaar bewerken",
|
||||
|
||||
@ -362,6 +362,9 @@
|
||||
"languageSelectLabel": "Język",
|
||||
"categories": {
|
||||
"infoSelectedNoOfMaxCategories": "{chosen} z {max} wybrane kategorie"
|
||||
},
|
||||
"teaserImage": {
|
||||
"cropperConfirm": "Potwierdzać"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,7 +203,10 @@
|
||||
},
|
||||
"contribution": {
|
||||
"edit": "Editar Contribuição",
|
||||
"delete": "Apagar Contribuição"
|
||||
"delete": "Apagar Contribuição",
|
||||
"teaserImage": {
|
||||
"cropperConfirm": "Confirmar"
|
||||
}
|
||||
},
|
||||
"comment": {
|
||||
"content": {
|
||||
|
||||
@ -44,7 +44,8 @@
|
||||
],
|
||||
"moduleNameMapper": {
|
||||
"^@/(.*)$": "<rootDir>/src/$1",
|
||||
"^~/(.*)$": "<rootDir>/$1"
|
||||
"^~/(.*)$": "<rootDir>/$1",
|
||||
"\\.(css|less)$": "identity-obj-proxy"
|
||||
},
|
||||
"testMatch": [
|
||||
"**/?(*.)+(spec|test).js?(x)"
|
||||
@ -62,6 +63,7 @@
|
||||
"apollo-cache-inmemory": "~1.6.3",
|
||||
"apollo-client": "~2.6.4",
|
||||
"cookie-universal-nuxt": "~2.0.18",
|
||||
"cropperjs": "^1.5.5",
|
||||
"cross-env": "~6.0.3",
|
||||
"date-fns": "2.4.1",
|
||||
"express": "~4.17.1",
|
||||
@ -121,6 +123,7 @@
|
||||
"eslint-plugin-vue": "~5.2.3",
|
||||
"flush-promises": "^1.0.2",
|
||||
"fuse.js": "^3.4.5",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "~24.9.0",
|
||||
"mutation-observer": "^1.0.3",
|
||||
"node-sass": "~4.12.0",
|
||||
@ -133,4 +136,4 @@
|
||||
"vue-svg-loader": "~0.12.0",
|
||||
"vue-template-compiler": "^2.6.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,6 +190,11 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.ds-card-image img {
|
||||
max-height: 2000px;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.masonry-grid {
|
||||
display: grid;
|
||||
grid-gap: 10px;
|
||||
|
||||
@ -200,8 +200,8 @@ export default {
|
||||
|
||||
.ds-card-image {
|
||||
img {
|
||||
max-height: 710px;
|
||||
object-fit: cover;
|
||||
max-height: 2000px;
|
||||
object-fit: contain;
|
||||
object-position: center;
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,9 +87,5 @@ export default {
|
||||
<style lang="scss">
|
||||
.related-post {
|
||||
box-shadow: $box-shadow-base;
|
||||
|
||||
.ds-card-image {
|
||||
max-height: 80px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -5538,6 +5538,11 @@ create-react-context@^0.2.1:
|
||||
fbjs "^0.8.0"
|
||||
gud "^1.0.0"
|
||||
|
||||
cropperjs@^1.5.5:
|
||||
version "1.5.6"
|
||||
resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-1.5.6.tgz#82faf432bec709d828f2f7a96d1179198edaf0e2"
|
||||
integrity sha512-eAgWf4j7sNJIG329qUHIFi17PSV0VtuWyAu9glZSgu/KlQSrfTQOC2zAz+jHGa5fAB+bJldEnQwvJEaJ8zRf5A==
|
||||
|
||||
cross-env@~6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-6.0.3.tgz#4256b71e49b3a40637a0ce70768a6ef5c72ae941"
|
||||
@ -7856,6 +7861,11 @@ hard-source-webpack-plugin@^0.13.1:
|
||||
webpack-sources "^1.0.1"
|
||||
write-json-file "^2.3.0"
|
||||
|
||||
harmony-reflect@^1.4.6:
|
||||
version "1.6.1"
|
||||
resolved "https://registry.yarnpkg.com/harmony-reflect/-/harmony-reflect-1.6.1.tgz#c108d4f2bb451efef7a37861fdbdae72c9bdefa9"
|
||||
integrity sha512-WJTeyp0JzGtHcuMsi7rw2VwtkvLa+JyfEKJCFyfcS0+CDkjQ5lHPu7zEhFZP+PDSRrEgXa5Ah0l1MbgbE41XjA==
|
||||
|
||||
has-ansi@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
|
||||
@ -8266,6 +8276,13 @@ icss-utils@^4.0.0, icss-utils@^4.1.1:
|
||||
dependencies:
|
||||
postcss "^7.0.14"
|
||||
|
||||
identity-obj-proxy@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14"
|
||||
integrity sha1-lNK9qWCERT7zb7xarsN+D3nx/BQ=
|
||||
dependencies:
|
||||
harmony-reflect "^1.4.6"
|
||||
|
||||
ieee754@^1.1.4:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
|
||||
@ -13618,11 +13635,6 @@ serve-static@1.14.1, serve-static@^1.14.1:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9"
|
||||
integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==
|
||||
dependencies:
|
||||
encodeurl "~1.0.2"
|
||||
escape-html "~1.0.3"
|
||||
parseurl "~1.3.3"
|
||||
send "0.17.1"
|
||||
|
||||
server-destroy@^1.0.1:
|
||||
version "1.0.1"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user