Closed ingmontoya closed 1 year ago
Hi @ingmontoya, yes it should be possible but it needs you to code it from the ground, as nothing like it is built-in. what do you have in mind? like the user clicks on a menu element then drags on the page to define an editable zone? should the zone be resizable? should it snap to a grid?
Hey @motla, thanks for your quick response!! Sounds good to have some sort of icon or menu item to define an editable zone, shouldn't be fixed, I'm intending to use it for full names, addresses, or email fields...
Im looking for the same thing! Anyone found a way?
Im getting closer but how can I change the caret position? Thanks a lot!
@ingmontoya
Sounds good to have some sort of icon or menu item to define an editable zone, shouldn't be fixed, I'm intending to use it for full names, addresses, or email fields...
Ok I see. You will have to fiddle a lot with HTML, CSS and JavaScript. In terms of page content management, most of it is managed by the web browser, so we are quite limited by HTML capabilities. So for instance if you want to set an element that can be moved inside the page, and the page content goes automatically around it, I think this is not easily feasible.
If you just want a field that stays "over" the page content, what you could do is to insert an HTML element (let's say a simple \<div> for instance) with CSS position:absolute
, either directly to the page HTML content
data (you can access it directly inside your Vue.js app), or at the current caret position with document.execCommand("insertHTML", false, "<div ...)
. To have drag&drop and resize capabilities you can change its CSS properties dynamically (left
, top
, width
, height
) by setting JavaScript mouse events on the \<div> itself and/or on a "resize handle" set inside the \<div>. You will have to limit these values so the user can't move the \<div> outside the page.
However I'm pretty sure absolute-positioned elements will not work well with the page split system of this library. It has not been designed for that and that would be quite a challenge...
@ariana-nemes
Im getting closer but how can I change the caret position? Thanks a lot!
Not trivial but apparently you can do it: https://stackoverflow.com/questions/6249095/how-to-set-the-caret-cursor-position-in-a-contenteditable-element-div
Thanks @motla!! I am trying to use document.execCommand("insertHTML", false, "<div ...) to insert some custom block elements.. But somehow it simply does not add the div that i want in the content. Is there a possibility to assist me further?
@ariana-nemes can you share me your code or your project?
No, because of some internal rules :(. But i can share some snippets with you!
async addTag(tag) {
document.execCommand(
'insertHTML',
false,
`<span contenteditable="false" class="template-document__tag" data-custom-field-type=${tag.value}><span contenteditable="false">${tag.name}</span></span>`,
);
},`
This function happens when i click on of the buttons attached in the screenshot.
@ariana-nemes is there an error on the web-browser console? did you try putting a console.log("test");
inside your addTag function to make sure it is executed when you click on your button?
No errors on the console. And yes, i'm 100% that it reaches that function.
<template>
<div class="main">
<!-- Top bar -->
<vue-file-toolbar-menu :content="menu" class="bar" />
<!-- Custom tags -->
<div class="template-document__tag-container">
<span v-for="(tag, index) in tags" :key="index">
<span
v-if="
(tag.type.includes(templateType) && parentTemplateType != 'EV' && parentTemplateType != 'OL') ||
tag.type.includes('ALL') ||
tag.type.includes(parentTemplateType)
"
>
<Tag
class="mr-2 template-document__tag-list-element"
:value="tag.name"
v-tooltip.top="`${locale.documents.this_field} ${tag.name.replace()}`"
@click="addTag(tag)"
></Tag>
</span>
</span>
</div>
<!-- Document editor -->
<vue-document-editor
class="editor"
id="document-editor"
ref="editor"
v-model:content="content"
:overlay="overlay"
:zoom="zoom"
:page_format_mm="page_format_mm"
:page_margins="page_margins"
:display="display"
/>
</div>
</template>
@ariana-nemes I think you missed the " " around ${tag.value} (it should be data-custom-field-type="${tag.value}"
) and make sure tag.value
/ tag.name
do not contain special characters or HTML. I saw from the spec the HTML content must be valid for insertHTML
to work, maybe this is the issue.
Thank but sadly this didn't fix the issue :(
@ariana-nemes also could you console.log(tag.name)
to make sure it is not empty in your function?
Yea. I think i found the issue.. Somehow the contenteditable is getting out of focus when im pressing one of my divs.
@motla do you have any idea how to refocus it?
@ariana-nemes Well I'm not sure what's going on... Here is an example that works:
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-document-editor@2/dist/VueDocumentEditor.umd.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vue-document-editor@2/dist/VueDocumentEditor.css" rel="stylesheet">
</head>
<body>
<div id="app">
<div>
<input v-for="(tag, index) in tags" :key="index" type="button" :value="tag.name" @click="addTag(tag)" />
</div>
<div style="font-family: Avenir, sans-serif">
<vue-document-editor v-model:content="content" />
</div>
</div>
<script>
const app = Vue.createApp({
components: { VueDocumentEditor },
data () {
return {
tags: [{ name: "Insert hello!", value: "hello!" }, { name: "Insert world!", value: "world!" }],
content: ["<h1>Hello!</h1>Fill this page with text and new pages will be created as it overflows."]
}
},
methods: {
async addTag(tag) {
document.execCommand('insertHTML', false, `<span>${tag.value}</span>`);
}
}
}).mount('#app');
</script>
</body>
</html>
From there you can get to your example step by step (or simplify your example step by step to get there) to understand what is wrong. I also noticed that setting <span contenteditable="false">${tag.value}</span>
inputs the value twice on Google Chrome, but not on Firefox and Safari... document.execCommand()
is not standard so we have to deal with it unfortunately... It's weird territory.
Good luck!
@ariana-nemes OK I found your issue, you were right about the fact that it unfocuses when you click on your button.
That is because inside your Tag
component, they use an element which is neither a <button>
or <input type="button">
, but likely a stylished <div>
.
In this case the behavior is that when the web browser detects a mousedown
event on any element, it unfocuses.
The mousedown
event is always triggered before the click
event (which is a combination of mousedown-mouseup).
A workaround is then to trigger your function using the @mousedown
event instead of the @click
one. So it will stay focused when document.execCommand
is called.
Then to prevent the unfocus you can call event.preventDefault()
inside your function.
By security, to prevent elements below your button (if any) to trigger a mousedown
event you can call event.stopPropagation()
Example here:
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue-document-editor@2/dist/VueDocumentEditor.umd.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/vue-document-editor@2/dist/VueDocumentEditor.css" rel="stylesheet">
<style>
.custom-btn {
font-family: sans-serif;
padding: 5px;
margin: 5px;
border: solid 1px black;
border-radius: 5px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="app">
<div>
<span v-for="(tag, index) in tags" :key="index" type="button" @mousedown="addTag(tag)" class="custom-btn">{{tag.name}}</span>
</div>
<div style="font-family: Avenir, sans-serif">
<vue-document-editor v-model:content="content" />
</div>
</div>
<script>
const app = Vue.createApp({
components: { VueDocumentEditor },
data () {
return {
tags: [{ name: "Insert hello!", value: "hello!" }, { name: "Insert world!", value: "world!" }],
content: ["<h1>Hello!</h1>Fill this page with text and new pages will be created as it overflows."]
}
},
methods: {
async addTag(tag) {
event.preventDefault();
event.stopPropagation();
document.execCommand('insertHTML', false, `<span>${tag.value}</span>`);
}
}
}).mount('#app');
</script>
</body>
</html>
I hope that it helps. I have to get back to my work now 😊 Good luck!
Is your feature request related to a problem? Please describe. No
Describe the solution you'd like in there a way to add dynamic editable fields? I'm wondering how I can add them to the editor so the user can add dynamic fields on demand.
Additional context Working on a contract template creator, but I want the user be able to create templates and add dynamic fields according to his needs.