Skip to content
Snippets Groups Projects
Commit 511aadcb authored by Valentin Rigal's avatar Valentin Rigal
Browse files

Build as a web extension

parent 67c6943e
No related branches found
No related tags found
2 merge requests!4Setup CI,!3Setup the stack to use Vue in both the web extension and a regular html form
......@@ -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
<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>
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");
};
......@@ -28,8 +28,7 @@
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/content.js"],
"css": ["css/content.css"]
"js": ["js/content.js"]
}
]
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment