I have a Vue component, “Add Dynamic Variable”. It consumes the prop “targetElementId” which is the text input (a div with contenteditable), and it has a dropdown, which when selected, adds a green span (dynamic variable label) to the input. How can I get it to add the variable where the typing cursor is instead of the end of the element?
<template>
<div class="relative inline-flex items-center">
<button type="button" @click="toggleDropdown" class="inline-flex items-center gap-x-1.5 rounded-full bg-green-100 hover:bg-green-50 px-2 py-1 text-xs font-medium text-green-700">
<BoltIcon class="h-3 w-3 fill-green-700" viewBox="0 0 12 12" aria-hidden="true"></BoltIcon>
Add Dynamic Variable
</button>
<div v-if="dropdownVisible" class="">
<div class="py-1 absolute right-0 z-10 mt-4 origin-top-right rounded-md bg-green-100 shadow-lg ring-1 ring-white/20 focus:outline-none">
<div v-for="variable in variables" :key="variable" class="pl-3 pr-4 py-2 text-xs flex flex-row font-medium text-green-700 cursor-pointer hover:bg-green-50" @click="selectVariable(variable)">
<BoltIcon class="h-3 w-3 mr-1 fill-green-700" viewBox="0 0 12 12" aria-hidden="true"></BoltIcon>
{{ variable }}
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { BoltIcon } from '@heroicons/vue/24/solid';
const props = defineProps({
variables: {
type: Array,
required: true,
},
targetElementId: {
type: String,
required: true,
},
});
const dropdownVisible = ref(false);
const inputField = ref(null);
const toggleDropdown = () => {
if (!dropdownVisible.value && inputField.value) {
inputField.value.focus();
}
dropdownVisible.value = !dropdownVisible.value;
};
const selectVariable = (variable) => {
const inputEl = inputField.value;
if (!inputEl) {
console.error(`Input field with id ${props.targetElementId} not found.`);
return;
}
const variableLabel = document.createElement('span');
variableLabel.className =
'inline-flex items-center gap-x-0.5 rounded-md bg-green-100 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20';
variableLabel.innerHTML =
`${variable}
<button type="button" class="group relative -mr-1 h-3.5 w-3.5 rounded-sm hover:bg-green-600/20">
<span class="sr-only">Remove</span>
<svg viewBox="0 0 14 14" class="h-3.5 w-3.5 stroke-green-700/50 group-hover:stroke-green-700/75">
<path d="M4 4l6 6m0-6l-6 6" />
</svg>
<span class="absolute -inset-1"></span>
</button>`;
variableLabel.setAttribute('contenteditable', 'false');
variableLabel.querySelector('button').addEventListener('click', () => {
variableLabel.remove();
});
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
range.deleteContents();
range.insertNode(variableLabel);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
} else {
inputEl.appendChild(variableLabel);
}
dropdownVisible.value = false;
};
onMounted(() => {
inputField.value = document.getElementById(props.targetElementId);
});
</script>
<style scoped>
</style>
I tried a saveCursorPosition feature