codex-team / editor.js

A block-style editor with clean JSON output
https://editorjs.io
Apache License 2.0
28.53k stars 2.08k forks source link

Using Vue in tool development? #765

Open butaminas opened 5 years ago

butaminas commented 5 years ago

I was wondering if anyone had some experiences or ideas on how could Vue be used for custom tool development?

I know there is a Vue wrapper for editor.js, but it's just a wrapper and I'm not looking for that, I'm looking to develop a custom tool that could utilize the reactivity of Vue.

Neophen commented 4 years ago

you can achieve this by doing something like this:

import CustomBlock from './CustomBlock.vue';
const ComponentCtor = Vue.extend(CustomBlock);

class SimpleImage {
  constructor({ data }: ImageProps) {
    this.data = data;
    this.wrapper = null;
  }

  static get toolbox() {
  // see the getting started tutorial;
  }

  render() {
    this.wrapper = document.createElement('div');
    this.wrapper.id = 'custom-id';
    // this you can use this like any other vue component.
    this.component = new ComponentCtor({
      propsData: this.data,
    });
    this.component.$mount('#custom-id');
    return this.wrapper;
  }

  save(blockContent) {
   // in the component have a function that returns the required data.
    return this.component.getData();
  }

  validate(savedData) {
    if (!savedData.url.trim()) {
      return false;
    }

    return true;
  }
}

i hope this is enough to get you started.

Devin345458 commented 4 years ago

I ended up doing the following

render() {
        this.wrapper = document.createElement('div');
        this.wrapper.id = 'custom-id';
        // this you can use this like any other vue component.
        this.component = new ComponentCtor({
            propsData: {
                filetypes: []
            },
        });
        this.component.$mount();
        return this.wrapper.appendChild(this.component.$el);
}
cannap commented 3 years ago

any know a way to access the current vue instance in the class maybe pass it via config or is there a other way? i use nuxt i dont have a way to import or export vue itself

//e found a other way i will make a config callback which is calling in the initalizer component then i will pass my data back

luckyboy07 commented 2 years ago

Is there any work around for this in the vue 3?

bettysteger commented 10 months ago

@luckyboy07 sorry for my late answer, i just did this in vue 3 using my own custom vue 3 component: (you need render and h from 'vue')

https://gist.github.com/bettysteger/d7f2b1a52bb1c23a0c24f3a9ff5832d9

luckyboy07 commented 10 months ago

@bettysteger Thank you so much, I will definitely try this one.

frankchen211 commented 2 days ago

Hi @bettysteger, I followed your code and succeded in using a custom component in the BlockTool, but there's another issue occured, not sure if you know how to fix it.

I put a primevue component in this custom component which is rendered by h() in blocktool class, but the inner primevue component throw an error:

Property "$primevue" was accessed during render but is not defined on instance.

Render method:

  render() {
    console.log('render');
    this.nodes.wrapper = document.createElement('div');
    this.nodes.wrapper.classList.add(styles['SceneHeading-tool']);

    const vueComponent = h(SceneHeadingComp, {
      showPopoverFn: this._settings.showPopoverFn,
      // onDataChanged: (event: any) => {
      //   console.log('outside onchange', event);
      // },
    });

    render(vueComponent, this.nodes.wrapper);

    return this.nodes.wrapper;
  }

The custom component:

<script setup lang="ts">
import { onMounted, ref } from 'vue';

import Listbox from 'primevue/listbox';

const selectedCity = ref();
const cities = ref([
  { name: 'New York', code: 'NY' },
  { name: 'Rome', code: 'RM' },
  { name: 'London', code: 'LDN' },
  { name: 'Istanbul', code: 'IST' },
  { name: 'Paris', code: 'PRS' },
]);

</script>

<template>
  <div>
    <Listbox
      v-model="selectedCity"
      :options="cities"
      class="w-full md:w-56"
      option-label="name"
    />
  </div>
</template>
bettysteger commented 1 day ago

Hi @bettysteger, I followed your code and succeded in using a custom component in the BlockTool, but there's another issue occured, not sure if you know how to fix it.

I put a primevue component in this custom component which is rendered by h() in blocktool class, but the inner primevue component throw an error:

Property "$primevue" was accessed during render but is not defined on instance.

Unfortunately i do not know how to fix it, because i have not used Primevue before. Is it possible to use their components outside of an vue app?

I created a few bootstrap-vue-next editorjs block tools and this was possible:

import { h, render, nextTick } from 'vue'
import { BModal } from 'bootstrap-vue-next';

// later in the render function 
h(BModal, { ... }
frankchen211 commented 21 hours ago

Hi @bettysteger, I followed your code and succeded in using a custom component in the BlockTool, but there's another issue occured, not sure if you know how to fix it. I put a primevue component in this custom component which is rendered by h() in blocktool class, but the inner primevue component throw an error:

Property "$primevue" was accessed during render but is not defined on instance.

Unfortunately i do not know how to fix it, because i have not used Primevue before. Is it possible to use their components outside of an vue app?

I created a few bootstrap-vue-next editorjs block tools and this was possible:

import { h, render, nextTick } from 'vue'
import { BModal } from 'bootstrap-vue-next';

// later in the render function 
h(BModal, { ... }

Finally I figure out the reason, there's no vue appContext from the rendered component which contains $primevue PrimeVue created in the root main.js.

onMounted(() => {
  const _this = getCurrentInstance();
  editor = new EditorJS({
    /**
     * Id of Element that should contain Editor instance
     */
    holder: 'editorjs',
    tools: {
      myTool: {
        class: MyTool,
        config: {
          vueInstance: () => _this,
        },
      },
vueInstance = this.config.vueInstance()
const vueComponent = h(MyVueCompoentWithPrimeVueCompInside, {...});

vueComponent.appContext = { ...vueInstance.appContext };
render(vueComponent, this.nodes.wrapper);