yikoyu / vuetify-pro-tiptap

A Rich Text Editor (WYSIWYG) for Vue3 with tiptap & Vuetify.
https://yikoyu.github.io/vuetify-pro-tiptap/
MIT License
169 stars 23 forks source link

How to integrate custom file upload with dropzone. #333

Open phyothiha opened 3 months ago

phyothiha commented 3 months ago

I will share how I integrated dropzone.js when I was working on this awesome package. Official dropzone.js is not maintained anymore, according to the issue#2288. So please use this package @deltablot/dropzone. The steps are very sample.

TLDR; (KeyPoint's)

  1. Emit a custom event with payload, that contains full url that is return from the server, to the parent element.
  2. Listen for the event and update computed form src value.

=================

1. Create a component that use dropzone.js

AppDropzone.vue

<script setup>
import { ref, onMounted } from "vue";
import { Dropzone } from "@deltablot/dropzone";
import "@deltablot/dropzone/dist/dropzone.css";

const emit = defineEmits(["upload"]);

const props = defineProps({
  url: {
    type: String,
    required: true,
  },
  method: {
    type: String,
    default: "post",
  },
  acceptedFiles: {
    type: String,
    default: "",
  },
  paramName: {
    type: String,
    default: "file",
  },
});

const dropzone = ref(null);

onMounted(() => {
  const dropzoneInstance = new Dropzone(dropzone.value, {
    url: "http://127.0.0.1:8000/api/v1/upload",
    headers: {
      Authorization:
        "Bearer 4|xcDCzwzWnq0KL9TFINS1l8iBN7xocaDyDRahMwHs819b6ed2",
    },
    method: props.method,
    paramName: props.paramName,
    acceptedFiles: props.acceptedFiles,
  });

  dropzoneInstance.on("success", function (file, response) {
    emit("upload", response.data?.path);
  });
});
</script>

<template>
  <div>
    <div ref="dropzone" class="dropzone"></div>
  </div>
</template>

2. Next, create a component and include dropzone component. I am not using typescript here.

AppFileUpload.vue

<script setup>
import { computed } from "vue";

const props = defineProps(["modelValue", "upload", "t"]);

const emit = defineEmits(["update:modelValue"]);

const form = computed({
  get: () => props.modelValue,
  set: (val) => {
    emit("update:modelValue", val);
  },
});

function onFileUploaded(url) {
  form.value.src = url;
}
</script>

<template>
  <AppDropzone
    url="/upload"
    v-model="form.src"
    @upload="onFileUploaded"
    class="mb-5"
  />

  <v-text-field v-model="form.alt" placeholder="Alt"></v-text-field>

  <v-checkbox
    v-model="form.lockAspectRatio"
    label="Lock original aspect ratio"
  ></v-checkbox>
</template>

3. Finally, use it in your form component

PostCreate.vue

<script setup>
import { markRaw } from "vue";
import { ref } from "vue";
import { BaseKit, Image } from "vuetify-pro-tiptap";
import AppFileUpload from "./components/AppFileUpload.vue";

const extensions = [
  BaseKit.configure({
    placeholder: {
      placeholder: "Write some text...",
    },
  }),
  Image.configure({
    imageTabs: [
      { name: "UPLOAD", component: markRaw(AppFileUpload) },
    ],
    hiddenTabs: ["upload"],
  }),
];

const content = ref("");
</script>

<template>
  <v-form>
    <VuetifyTiptap
      v-model="content"
      :min-height="200"
      :max-height="465"
      :max-width="900"
      :extensions="extensions"
    />
  </v-form>
</template>