Stimulusでドラッグ&ドロップ時に画像のアップロードを行う

はてなフォトっぽいやつを実装してみます。

やり方

view

formのinput file は非表示にしておきます(d-noneスタイル)。
Stimulusのcontrollerは file-drop-controller というものを用意し、ドラッグ&ドロップの操作はここで行うようにします。
click時の操作は元々用意していた preview-controller にて実施します。

# haml
      .uploading-image.pt-3
        = form_with url: user_images_path(@user), local: true, data: { controller: "preview file-drop" } do |f|
          .input-group.d-none
            = f.file_field :images, class: "form-control override-bs-file image-selection", data: {"action": "input->preview#show", "file-drop-target": "file", "preview-target": "file" }          .drag-and-drop-image.mt-2
            .drag-and-drop-image-message{"data-action": "click->preview#selectImage dragover->file-drop#dragover dragleave->file-drop#dragleave drop->file-drop#drop drop->preview#show"}
              クリックしてファイルを選択するか、ここにファイルをドラッグしてください

Stimulus

簡単なclick時の操作から。
上記のようにviewとしては、Stimulusで扱えるようにinput fileにtargetを追加し、clickイベント発生時に selectImage actionを呼ぶようにしておきます。
Stimulusでは click() を実行するだけ。

export default class extends Controller {
  static targets = ["content", "modal", "file"]

  selectImage() {
    this.fileTarget.click()
  }
}

続いてドラッグ&ドロップ。 こちらもviewでdragover, dragleave, dropイベントを用意します。
drop後にはmodalでpreview表示するために、preview#show も実行するようにしています。)
Stimulusではcssで背景を変更するためのstyleの追加・削除を行い、drop時にinput fileへの設定を行なっているだけです。

// app/javascript/controllers/file_drop_controller.js
import { Controller } from '@hotwired/stimulus'

export default class extends Controller {
  static targets = ["file"]

  dragover(e) {
    e.preventDefault()
    e.target.classList.add("dragging")
  }

  dragleave(e) {
    e.preventDefault()
    e.target.classList.remove("dragging")
  }

  drop(e) {
    e.preventDefault()
    e.target.classList.remove("dragging")

    const files = e.dataTransfer.files
    if (typeof files[0] !== 'undefined') {
      this.fileTarget.files = files
    }
  }
}
//scss

  // uploading image
  .drag-and-drop-image {
    border: 2px dashed silver;
    cursor: pointer;
    font-size: 1.2em;
    text-align: center;

    .drag-and-drop-image-message.dragging {
      background-color: rgb(233, 233, 233);
    }
  }

感想

まぁ、Stimlus難しくはないんですよね。単純なやつならいいかなという気はするのですが、もう少しcontrollerとactionが増えてくると辛みがありそうな気がします。

See Also

cropper.jsを使ってActiveStorageで画像アップロード時に切り抜きを行う
プレビューの実装はこちら

参考

Stimulusを使ってドラッグ&ドロップでファイルアップロード - Qiita