Skip to content

Vue ​

v-model:document via update:document.

vue
<script setup>
import { onMounted, onBeforeUnmount, ref } from "vue";
import { mountVanillaEditor } from "velo-text";

const props = defineProps({
  document: { type: Object, required: true },
  theme: { type: String, default: "light-neutral" },
  editable: { type: Boolean, default: true },
  resolveAssetUrl: { type: Function, default: undefined },
  getVariableCatalog: { type: Function, default: undefined },
  getTemplateData: { type: Function, default: undefined },
});
const emit = defineEmits(["update:document"]);
const el = ref(null);
let editor;

onMounted(() => {
  editor = mountVanillaEditor(el.value, {
    document: props.document,
    theme: props.theme,
    editable: props.editable,
    resolveAssetUrl: props.resolveAssetUrl,
    getVariableCatalog: props.getVariableCatalog,
    getTemplateData: props.getTemplateData,
    onChange: (doc) => emit("update:document", doc),
  });
});
onBeforeUnmount(() => editor?.destroy());

defineExpose({
  insertVariable: (path, format, fallback) => editor?.commands.insertVariable(path, format, fallback),
  getDocument: () => editor?.getDocument(),
  commands: () => editor?.commands,
});
</script>
<template>
  <div ref="el" class="pde-editor"></div>
</template>
vue
<script setup>
import { ref } from "vue";
import { createDocument, createIdGenerator, exportDocument } from "velo-text";
import PortableEditor from "./PortableEditor.vue";

const MIME = {
  pdf: "application/pdf",
  odt: "application/vnd.oasis.opendocument.text",
  docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
};

function sampleDoc() {
  const g = createIdGenerator("vue");
  const doc = createDocument({ idGenerator: g, clock: { nowIso: () => new Date().toISOString() } });
  doc.root.children.push({
    type: "paragraph",
    id: g.next(),
    children: [
      { type: "text", id: g.next(), text: "Hello " },
      { type: "variable", id: g.next(), path: "name", source: "{{name}}", valueType: "string" },
    ],
  });
  return doc;
}

const editor = ref(null);
const document = ref(sampleDoc());
const status = ref("ready");

async function doExport(format) {
  const live = editor.value?.getDocument() ?? document.value;
  const chunks = [];
  await exportDocument({
    document: live,
    data: { name: "Ada" },
    format,
    sink: { write(c) { chunks.push(c); }, close() {} },
    options: { strict: false },
  });
  const a = window.document.createElement("a");
  a.href = URL.createObjectURL(new Blob(chunks, { type: MIME[format] }));
  a.download = `vue.${format}`;
  a.click();
  status.value = `exported ${format}`;
}
</script>
<template>
  <div data-pde-theme="light-neutral">
    <p>{{ status }}</p>
    <button type="button" @click="editor?.insertVariable('name')">Insert variable</button>
    <button type="button" @click="doExport('pdf')">PDF</button>
    <button type="button" @click="doExport('odt')">ODT</button>
    <button type="button" @click="doExport('docx')">DOCX</button>
    <PortableEditor ref="editor" v-model:document="document" theme="light-neutral" />
  </div>
</template>

Zero runtime dependencies — TypeScript strict — MIT