aboutsummaryrefslogtreecommitdiff
path: root/src/components/image_cropper
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/image_cropper')
-rw-r--r--src/components/image_cropper/image_cropper.js128
-rw-r--r--src/components/image_cropper/image_cropper.vue42
2 files changed, 170 insertions, 0 deletions
diff --git a/src/components/image_cropper/image_cropper.js b/src/components/image_cropper/image_cropper.js
new file mode 100644
index 00000000..49d51846
--- /dev/null
+++ b/src/components/image_cropper/image_cropper.js
@@ -0,0 +1,128 @@
+import Cropper from 'cropperjs'
+import 'cropperjs/dist/cropper.css'
+
+const ImageCropper = {
+ props: {
+ trigger: {
+ type: [String, window.Element],
+ required: true
+ },
+ submitHandler: {
+ type: Function,
+ required: true
+ },
+ cropperOptions: {
+ type: Object,
+ default () {
+ return {
+ aspectRatio: 1,
+ autoCropArea: 1,
+ viewMode: 1,
+ movable: false,
+ zoomable: false,
+ guides: false
+ }
+ }
+ },
+ mimes: {
+ type: String,
+ default: 'image/png, image/gif, image/jpeg, image/bmp, image/x-icon'
+ },
+ saveButtonLabel: {
+ type: String
+ },
+ cancelButtonLabel: {
+ type: String
+ }
+ },
+ data () {
+ return {
+ cropper: undefined,
+ dataUrl: undefined,
+ filename: undefined,
+ submitting: false,
+ submitError: null
+ }
+ },
+ computed: {
+ saveText () {
+ return this.saveButtonLabel || this.$t('image_cropper.save')
+ },
+ cancelText () {
+ return this.cancelButtonLabel || this.$t('image_cropper.cancel')
+ },
+ submitErrorMsg () {
+ return this.submitError && this.submitError instanceof Error ? this.submitError.toString() : this.submitError
+ }
+ },
+ methods: {
+ destroy () {
+ if (this.cropper) {
+ this.cropper.destroy()
+ }
+ this.$refs.input.value = ''
+ this.dataUrl = undefined
+ this.$emit('close')
+ },
+ submit () {
+ this.submitting = true
+ this.avatarUploadError = null
+ this.submitHandler(this.cropper, this.file)
+ .then(() => this.destroy())
+ .catch((err) => {
+ this.submitError = err
+ })
+ .finally(() => {
+ this.submitting = false
+ })
+ },
+ pickImage () {
+ this.$refs.input.click()
+ },
+ createCropper () {
+ this.cropper = new Cropper(this.$refs.img, this.cropperOptions)
+ },
+ getTriggerDOM () {
+ return typeof this.trigger === 'object' ? this.trigger : document.querySelector(this.trigger)
+ },
+ readFile () {
+ const fileInput = this.$refs.input
+ if (fileInput.files != null && fileInput.files[0] != null) {
+ this.file = fileInput.files[0]
+ let reader = new window.FileReader()
+ reader.onload = (e) => {
+ this.dataUrl = e.target.result
+ this.$emit('open')
+ }
+ reader.readAsDataURL(this.file)
+ this.$emit('changed', this.file, reader)
+ }
+ },
+ clearError () {
+ this.submitError = null
+ }
+ },
+ mounted () {
+ // listen for click event on trigger
+ const trigger = this.getTriggerDOM()
+ if (!trigger) {
+ this.$emit('error', 'No image make trigger found.', 'user')
+ } else {
+ trigger.addEventListener('click', this.pickImage)
+ }
+ // listen for input file changes
+ const fileInput = this.$refs.input
+ fileInput.addEventListener('change', this.readFile)
+ },
+ beforeDestroy: function () {
+ // remove the event listeners
+ const trigger = this.getTriggerDOM()
+ if (trigger) {
+ trigger.removeEventListener('click', this.pickImage)
+ }
+ const fileInput = this.$refs.input
+ fileInput.removeEventListener('change', this.readFile)
+ }
+}
+
+export default ImageCropper
diff --git a/src/components/image_cropper/image_cropper.vue b/src/components/image_cropper/image_cropper.vue
new file mode 100644
index 00000000..24a6f3bd
--- /dev/null
+++ b/src/components/image_cropper/image_cropper.vue
@@ -0,0 +1,42 @@
+<template>
+ <div class="image-cropper">
+ <div v-if="dataUrl">
+ <div class="image-cropper-image-container">
+ <img ref="img" :src="dataUrl" alt="" @load.stop="createCropper" />
+ </div>
+ <div class="image-cropper-buttons-wrapper">
+ <button class="btn" type="button" :disabled="submitting" @click="submit" v-text="saveText"></button>
+ <button class="btn" type="button" :disabled="submitting" @click="destroy" v-text="cancelText"></button>
+ <i class="icon-spin4 animate-spin" v-if="submitting"></i>
+ </div>
+ <div class="alert error" v-if="submitError">
+ {{submitErrorMsg}}
+ <i class="button-icon icon-cancel" @click="clearError"></i>
+ </div>
+ </div>
+ <input ref="input" type="file" class="image-cropper-img-input" :accept="mimes">
+ </div>
+</template>
+
+<script src="./image_cropper.js"></script>
+
+<style lang="scss">
+.image-cropper {
+ &-img-input {
+ display: none;
+ }
+
+ &-image-container {
+ position: relative;
+
+ img {
+ display: block;
+ max-width: 100%;
+ }
+ }
+
+ &-buttons-wrapper {
+ margin-top: 15px;
+ }
+}
+</style>