<template> <div class="virtual-keyboard"> <!-- Allow focus on the keyboard to automatically restore focus on the input --> <section tabindex="0"> <button type="button" v-on:mouseup="addChar('ȵ')">ȵ</button> <button type="button" v-on:mouseup="addChar('Ȫ')">Ȫ</button> <button type="button" v-on:mouseup="addChar('ɀ')">ɀ</button> <button type="button" v-on:mouseup="addChar('Ɇ')">Ɇ</button> <a v-if="manager" v-on:click.prevent="optionsModal = true" href="#" >Options</a > </section> <Modal v-if="optionsModal" v-model="optionsModal"> <span>option 1: </span> <input /> </Modal> </div> </template> <script> import Modal from "./Modal.vue"; export default { components: { Modal, }, props: { inputField: { type: [HTMLInputElement, HTMLTextAreaElement], required: true, }, manager: { type: Boolean, default: false, }, }, data: () => ({ keysSwitch: { a: "ǻ", A: "Ǡ", }, optionsModal: false, }), created() { this.inputField.addEventListener("blur", this.looseFocus); this.inputField.addEventListener("keydown", this.keyInput); }, beforeDestroy() { this.inputField.removeEventListener("blur", this.looseFocus); this.inputField.removeEventListener("keydown", this.keyInput); }, methods: { addChar(char) { // Add a character to the input depending on the cursor selection const start = this.inputField.selectionStart; const value = this.inputField.value; this.inputField.value = value.slice(0, start) + char + value.slice(this.inputField.selectionEnd); // Reset the input caret after modifying the input value this.inputField.selectionStart = this.inputField.selectionEnd = start + 1; }, keyInput(e) { const character = this.keysSwitch[e.key]; if (!character) return; this.addChar(character); e.preventDefault(); }, looseFocus() { if (this.optionsModal) return; // We need a short delay before checking where the user moved the focus setTimeout(() => { // In case the user typed on the keyboard, restore focus on the input if (this.$el.contains(document.activeElement)) return this.inputField.focus(); // Otherwise emit a custom event on the input for the keyboard to be removed this.inputField.dispatchEvent( new CustomEvent("vk_rmkeyboard", { detail: this }) ); }, 1); }, }, watch: { optionsModal(value) { // Focus the input when closing the modal if (!value) this.inputField.focus(); }, }, }; </script> <style scoped> .virtual-keyboard { /* Avoid properties inheritance */ all: initial; /* Override required attributes */ position: absolute; display: flex; /* Arbitrary z-index */ z-index: 99999; width: 20rem; } .virtual-keyboard > section { /* reset style in order to use Pure CSS framework only */ all: unset; width: 100%; background-color: white; border: solid #bfbfbf 1px; border-radius: 0.25rem; padding: 0.25rem; } button { width: 3rem; height: 3rem; margin: 0.1rem; } </style>