diff --git a/app/components/avo/fields/markdown_field/edit_component.html.erb b/app/components/avo/fields/markdown_field/edit_component.html.erb index 51724f4e2..a86f879fb 100644 --- a/app/components/avo/fields/markdown_field/edit_component.html.erb +++ b/app/components/avo/fields/markdown_field/edit_component.html.erb @@ -7,6 +7,7 @@ view: view, 'easy-mde-target': 'element', 'component-options': @field.options.to_json, + upload_url: @field.options[:upload_url] }, disabled: disabled?, placeholder: @field.placeholder, diff --git a/app/javascript/js/controllers/fields/easy_mde_controller.js b/app/javascript/js/controllers/fields/easy_mde_controller.js index 3d504af39..48947beef 100644 --- a/app/javascript/js/controllers/fields/easy_mde_controller.js +++ b/app/javascript/js/controllers/fields/easy_mde_controller.js @@ -1,5 +1,6 @@ import { Controller } from '@hotwired/stimulus' import EasyMDE from 'easymde' +import { DirectUpload } from '@rails/activestorage' export default class extends Controller { static targets = ['element'] @@ -17,6 +18,7 @@ export default class extends Controller { } connect() { + const options = { element: this.elementTarget, spellChecker: this.componentOptions.spell_checker, @@ -28,9 +30,76 @@ export default class extends Controller { options.status = false } + + if (this.componentOptions.image_upload) { + this.#configureImageUploads(options) + } + const easyMde = new EasyMDE(options) if (this.view === 'show') { easyMde.codemirror.options.readOnly = true } } + + #configureImageUploads(options) { + options.uploadImage = true + options.imageUploadEndpoint = this.elementTarget.dataset.uploadUrl + options.imageUploadFunction = this.#handleImageUpload.bind(this) + options.imageAccept = 'image/*' + options.previewImagesInEditor = true + options.toolbar = this.toolbarItems + return options + } + + #handleImageUpload(file, onSuccess, onError) { + const upload = new DirectUpload(file, this.elementTarget.dataset.uploadUrl) + upload.create((error, blob) => { + if (error) return onError(error) + const imageUrl = this.#encodedImageUrl(blob) + onSuccess(imageUrl) + }) + } + + #encodedImageUrl(blob) { + return `/rails/active_storage/blobs/redirect/${ + blob.signed_id + }/${encodeURIComponent(blob.filename)}` + } + + get toolbarItems() { + const baseItems = [ + 'bold', + 'italic', + 'heading', + '|', + 'quote', + 'unordered-list', + 'ordered-list', + '|', + 'link', + 'image', + ] + + const uploadImageItem = this.componentOptions.image_upload + ? [ + { + name: 'upload-image', + action: EasyMDE.drawUploadedImage, + className: 'fa fa-file-picture-o', + title: 'Upload & insert image', + }, + ] + : [] + + return [ + ...baseItems, + ...uploadImageItem, + '|', + 'preview', + 'side-by-side', + 'fullscreen', + '|', + 'guide', + ] + } } diff --git a/lib/avo/fields/markdown_field.rb b/lib/avo/fields/markdown_field.rb index 4ca474201..c5e4dea60 100644 --- a/lib/avo/fields/markdown_field.rb +++ b/lib/avo/fields/markdown_field.rb @@ -9,14 +9,26 @@ def initialize(id, **args, &block) hide_on :index @always_show = args[:always_show].present? ? args[:always_show] : false - @height = args[:height].present? ? args[:height].to_s : "auto" - @spell_checker = args[:spell_checker].present? ? args[:spell_checker] : false + @height = args[:height].present? ? args[:height].to_s : 'auto' + @upload_url = args[:upload_url].present? ? args[:upload_url] : direct_uploads_url + @image_upload = + args[:image_upload].present? ? args[:image_upload] : false + @spell_checker = + args[:spell_checker].present? ? args[:spell_checker] : false @options = { spell_checker: @spell_checker, always_show: @always_show, + image_upload: @image_upload, + upload_url: direct_uploads_url, height: @height } end + + private + + def direct_uploads_url + "/rails/active_storage/direct_uploads" + end end end end diff --git a/spec/dummy/app/avo/resources/project.rb b/spec/dummy/app/avo/resources/project.rb index 9d8d70f80..27ac7f809 100644 --- a/spec/dummy/app/avo/resources/project.rb +++ b/spec/dummy/app/avo/resources/project.rb @@ -60,7 +60,7 @@ def fields relative: true, timezone: "EET", format: "MMMM dd, y HH:mm:ss z" - field :description, as: :markdown, height: "350px" + field :description, as: :markdown, height: "350px", image_upload: true field :files, as: :files, translation_key: "avo.field_translations.files",