import { Controller } from '@hotwired/stimulus' import { Dropzone } from 'dropzone' import Cropper from 'cropperjs' export default class extends Controller { static values = { avatarImage: String, userId: Number } connect() { let avatarDropzone = new Dropzone('#avatarDropzone', { url: `/user/upload/avatar/${this.userIdValue}`, avatarUrl: this.avatarImageValue, acceptedFiles: '.jpg, .jpeg, .png, gif', maxFiles: 1, dictDefaultMessage: '', init() { avatarDropzone = this // If the thumbnail is already in the right size on your server: const mockFile = { name: 'Filename', size: 12345 } const callback = null // Optional callback when it's done const crossOrigin = null // Added to the `img` tag for crossOrigin handling const resizeThumbnail = false // Tells Dropzone whether it should resize the image first avatarDropzone.displayExistingFile( mockFile, avatarDropzone.options.avatarUrl, callback, crossOrigin, resizeThumbnail ) avatarDropzone.files.push(mockFile) // line missing in official docs }, transformFile(file, done) { // Create the image editor overlay const editor = document.createElement('div') editor.style.position = 'fixed' editor.style.left = '25px' editor.style.right = '25px' editor.style.top = '25px' editor.style.bottom = '25px' editor.style.zIndex = '9999' editor.style.borderRadius = '15px;' editor.style.borderColor = '#FF8844' editor.style.backgroundColor = '#000' document.body.appendChild(editor) // Create an image node for Cropper.js const image = new Image() image.src = URL.createObjectURL(file) editor.appendChild(image) // Create Cropper.js const cropper = new Cropper(image, { aspectRatio: 1 }) // Create confirm button at the top left of the viewport const buttonCrop = document.createElement('button') buttonCrop.style.position = 'absolute' buttonCrop.style.right = '10px' buttonCrop.style.bottom = '50px' buttonCrop.style.zIndex = '9999' buttonCrop.textContent = 'Crop' buttonCrop.className = 'btn btn-secondary' editor.appendChild(buttonCrop) buttonCrop.addEventListener('click', () => { // clear preview //formElement.style.backgroundImage = '' const canvas = cropper.getCroppedCanvas({ width: 256, height: 256 }) canvas.toBlob((blob) => { avatarDropzone.createThumbnail( blob, avatarDropzone.options.thumbnailWidth, avatarDropzone.options.thumbnailHeight, avatarDropzone.options.thumbnailMethod, false, (dataURL) => { avatarDropzone.emit('thumbnail', file, dataURL) done(blob) } ) }) // Remove the editor from the view document.body.removeChild(editor) }) // Create keep original button const buttonKeep = document.createElement('button') buttonKeep.style.position = 'absolute' buttonKeep.style.right = '100px' buttonKeep.style.bottom = '50px' buttonKeep.style.zIndex = '9999' buttonKeep.textContent = 'Keep Original' buttonKeep.className = 'btn btn-primary' buttonKeep.focus() editor.appendChild(buttonKeep) buttonKeep.addEventListener('click', () => { //formElement.style.backgroundImage = '' // Remove the editor from the view this.removeAllFiles() done(file) document.body.removeChild(editor) }) // info text const infoText = document.createElement('span') infoText.style.position = 'absolute' infoText.style.opacity = '0.5' infoText.style.left = '100px' infoText.style.bottom = '50px' infoText.style.display = 'inline:block' infoText.style.zIndex = '9999' infoText.textContent = 'Use mouse wheel/touchpad to adapt size' infoText.className = 'btn btn-secondary' editor.appendChild(infoText) } }) } }