From 511aadcb967ce632c8f49251db8ba527b66c24be Mon Sep 17 00:00:00 2001 From: Valentin Rigal <rigal@teklia.com> Date: Thu, 3 Jun 2021 16:40:51 +0200 Subject: [PATCH] Build as a web extension --- README.md | 11 ++++ src/components/KeyboardDisplay.vue | 51 ++++++++++++++++++ src/content/content.js | 84 +++++++++++++----------------- src/manifest.json | 3 +- 4 files changed, 98 insertions(+), 51 deletions(-) create mode 100644 src/components/KeyboardDisplay.vue diff --git a/README.md b/README.md index fb25430a..02b8d725 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,14 @@ ## Development `npm run serve` + +## Run the application as a web extension +### Firefox +* Build the packaged addon `npm run build` +* Go to `about:debugging#/runtime/this-firefox` +* Load `artifacts/virtual-keyboard-v<version>-production.zip` as a temporary extension + +### Chromium +* Run `npm run serve` to support live reload +* Go to `chrome://extensions/` +* Click on "Load unpacked" and select the `dist` folder diff --git a/src/components/KeyboardDisplay.vue b/src/components/KeyboardDisplay.vue new file mode 100644 index 00000000..61a98683 --- /dev/null +++ b/src/components/KeyboardDisplay.vue @@ -0,0 +1,51 @@ +<template> + <div v-on:click.prevent class="virtual-keyboard"> + <button v-on:mouseup="addChar('a')">Insert character "a"</button> + <button v-on:mouseup="addChar('b')">Insert character "b"</button> + </div> +</template> + +<script> +export default { + props: { + inputField: { + type: HTMLInputElement, + required: true, + }, + }, + mounted() { + this.inputField.onkeydown = (e) => { + const replacements = { a: "b" }; + const character = replacements[e.key]; + if (!character) return; + this.addChar(character); + return false; + }; + }, + methods: { + addChar(char) { + console.log("adding", char); + // Add a character to the input depending on selection position + 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; + }, + }, +}; +</script> + +<style lang="scss" scoped> +.virtual-keyboard { + position: absolute; + top: 100%; + display: flex; +} +button { + padding: 1rem; +} +</style> diff --git a/src/content/content.js b/src/content/content.js index cbd64fab..777fc119 100644 --- a/src/content/content.js +++ b/src/content/content.js @@ -1,58 +1,44 @@ -const inputFields = document.getElementsByTagName("input"); -const replacements = { - a: "b", -}; -// Create a simple keyboard -const buttonA = document.createElement("button"); -buttonA.textContent = 'Insert "a"'; -const buttonB = document.createElement("button"); -buttonB.textContent = 'Insert "b"'; -const keyboard = document.createElement("div"); -keyboard.appendChild(buttonA); -keyboard.appendChild(buttonB); +import Vue from "vue"; +import Keyboard from "../components/KeyboardDisplay.vue"; +import router from "../router"; +import store from "../store"; -let selectedInput = null; +const inputFields = document.getElementsByTagName("input"); +let keyboard = null; -const addChar = (i, char) => { - const start = i.selectionStart; - i.value = i.value.slice(0, start) + char + i.value.slice(i.selectionEnd); - // Updating the input value moves the caret to the end by default - i.selectionStart = i.selectionEnd = start + 1; +const createKeyboard = (input) => { + const keyboardDiv = document.createElement("div"); + input.parentElement.appendChild(keyboardDiv); + keyboard = new Vue({ + router, + store, + render: (h) => { + return h(Keyboard, { + props: { inputField: input }, + }); + }, + el: keyboardDiv, + }); }; for (const input of inputFields) { - input.onkeydown = (e) => { - const character = replacements[e.key]; - if (!character) return; - addChar(input, character); - return false; - }; - // Handle the case where the input is already focused - if (document.activeElement === input) { - input.parentElement.appendChild(keyboard); - selectedInput = input; - } + // Handle the case where the input is already focused when this script is loaded + if (!keyboard && document.activeElement === input) createKeyboard(input); + input.onfocus = () => { - input.parentElement.appendChild(keyboard); - selectedInput = input; + // TODO it is not possible to go from an input field to another with the keyboard global + if (!keyboard) createKeyboard(input); }; - // https://css-tricks.com/a-css-approach-to-trap-focus-inside-of-an-element/ - input.ontransitionend = (e) => { - if (input.matches(":focus") || keyboard.contains(document.activeElement)) - e.target.focus(); - else { - input.parentElement.removeChild(keyboard); - selectedInput = null; - } + input.onblur = (e) => { + if (!keyboard) return; + // This is a hacky way to keep the focus on the input + setTimeout(() => { + if (keyboard.$el.contains(document.activeElement)) e.target.focus(); + else { + keyboard.$destroy(); + keyboard.$el.parentElement.removeChild(keyboard.$el); + keyboard = null; + } + }, 1); }; } - -keyboard.onclick = () => { - return false; -}; -buttonA.onmouseup = () => { - if (selectedInput) addChar(selectedInput, "a"); -}; -buttonB.onmouseup = () => { - if (selectedInput) addChar(selectedInput, "b"); -}; diff --git a/src/manifest.json b/src/manifest.json index e986c126..7962da25 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -28,8 +28,7 @@ "content_scripts": [ { "matches": ["<all_urls>"], - "js": ["js/content.js"], - "css": ["css/content.css"] + "js": ["js/content.js"] } ] } -- GitLab