use own keyboard and improve css
This commit is contained in:
parent
0f881b668c
commit
fa6757b221
9 changed files with 233 additions and 161 deletions
|
@ -15,7 +15,5 @@
|
|||
"webpack": "^5.61.0",
|
||||
"webpack-cli": "^4.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"simple-keyboard": "^3.3.16"
|
||||
}
|
||||
"dependencies": {}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ class Banner {
|
|||
return; // Already mounted
|
||||
|
||||
this.element = document.createRange().createContextualFragment(`
|
||||
<div class="card small-card center-text">
|
||||
<div class="Card Card-ui-bottom Card-centered-text">
|
||||
<h2 id="banner-title"></h2>
|
||||
<p id="banner-text"></p>
|
||||
</div>
|
||||
|
|
|
@ -1,90 +1,122 @@
|
|||
import SimpleKeyboard from "simple-keyboard";
|
||||
|
||||
class KeyboardController {
|
||||
constructor(connection) {
|
||||
this.connection = connection;
|
||||
this.keyboard = null;
|
||||
this.keyboardDiv = null;
|
||||
this.container = null;
|
||||
|
||||
this.layouts = {
|
||||
default: [
|
||||
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"],
|
||||
["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"],
|
||||
["a", "s", "d", "f", "g", "h", "j", "k", "l"],
|
||||
["🄰", "z", "x", "c", "v", "b", "n", "m", ".", "⌦"],
|
||||
["🔢", " ", "↵"]
|
||||
],
|
||||
uppercase: [
|
||||
["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"],
|
||||
["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"],
|
||||
["A", "S", "D", "F", "G", "H", "J", "K", "L"],
|
||||
["🅰", "Z", "X", "C", "V", "B", "N", "M", ",", "⌦"],
|
||||
["🔢", " ", "↵"]
|
||||
],
|
||||
symbols: [
|
||||
["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"],
|
||||
["[", "]", "{", "}", ";", ":", "'", "\"", ",", "<"],
|
||||
[".", ">", "-", "_", "=", "+", "/", "?", "\\"],
|
||||
["🄰", "|", "`", "~", "⌦"],
|
||||
["🔤", " ", "↵"]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
_sendKeyPress(l) {
|
||||
this.connection.sendMessage("k", [l]);
|
||||
}
|
||||
|
||||
makeKeyboardForLayout(layout) {
|
||||
queueMicrotask(() => {
|
||||
while (this.container.firstChild) {
|
||||
this.container.removeChild(this.container.lastChild);
|
||||
}
|
||||
|
||||
const rowsElement = document.createElement("div");
|
||||
rowsElement.classList.add("Keyboard-rows");
|
||||
|
||||
for (let row = 0; row < layout.length; row++) {
|
||||
const rowElement = document.createElement("div");
|
||||
rowElement.classList.add("Keyboard-row");
|
||||
for (let col = 0; col < layout[row].length; col++) {
|
||||
const key = layout[row][col];
|
||||
|
||||
const colElement = document.createElement("button");
|
||||
colElement.classList.add("Keyboard-button");
|
||||
colElement.innerHTML = key;
|
||||
colElement.addEventListener("click", (event) => {
|
||||
this.handleKeypress(key);
|
||||
});
|
||||
|
||||
rowElement.appendChild(colElement);
|
||||
}
|
||||
rowsElement.appendChild(rowElement);
|
||||
}
|
||||
|
||||
this.container.appendChild(rowsElement);
|
||||
});
|
||||
}
|
||||
|
||||
handleKeypress(key) {
|
||||
switch (key) {
|
||||
case "🄰": {
|
||||
this.makeKeyboardForLayout(this.layouts.uppercase);
|
||||
break;
|
||||
}
|
||||
case "🅰": {
|
||||
this.makeKeyboardForLayout(this.layouts.default);
|
||||
break;
|
||||
}
|
||||
case "🔢": {
|
||||
this.makeKeyboardForLayout(this.layouts.symbols);
|
||||
break;
|
||||
}
|
||||
case "🔤": {
|
||||
this.makeKeyboardForLayout(this.layouts.default);
|
||||
break;
|
||||
}
|
||||
case " ": {
|
||||
this._sendKeyPress("{space}");
|
||||
break;
|
||||
}
|
||||
case "⌦": {
|
||||
this._sendKeyPress("{backspace}");
|
||||
break;
|
||||
}
|
||||
case "↵": {
|
||||
this._sendKeyPress("{ent}");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
this._sendKeyPress(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mountOn(element) {
|
||||
if (this.keyboardDiv)
|
||||
if (this.container)
|
||||
return; // Already mounted;
|
||||
|
||||
this.keyboardDiv = document.createElement("div");
|
||||
this.keyboardDiv.classList.add("keyboard");
|
||||
element.appendChild(this.keyboardDiv);
|
||||
this.container = document.createElement("div");
|
||||
this.container.classList.add("Keyboard");
|
||||
element.appendChild(this.container);
|
||||
|
||||
this.keyboard = new SimpleKeyboard(this.keyboardDiv, {
|
||||
onKeyPress: this.onKeyPress.bind(this),
|
||||
mergeDisplay: true,
|
||||
layoutName: "default",
|
||||
// refer to: https://hodgef.com/simple-keyboard/demos/?d=mobile
|
||||
layout: {
|
||||
default: [
|
||||
"q w e r t y u i o p",
|
||||
"a s d f g h j k l",
|
||||
"{shift} z x c v b n m {backspace}",
|
||||
"{numbers} {space} {ent}"
|
||||
],
|
||||
shift: [
|
||||
"Q W E R T Y U I O P",
|
||||
"A S D F G H J K L",
|
||||
"{shift} Z X C V B N M {backspace}",
|
||||
"{numbers} {space} {ent}"
|
||||
],
|
||||
numbers: ["1 2 3", "4 5 6", "7 8 9", "{abc} 0 {backspace}"]
|
||||
},
|
||||
display: {
|
||||
"{numbers}": "123",
|
||||
"{ent}": "return",
|
||||
"{escape}": "esc ⎋",
|
||||
"{tab}": "tab ⇥",
|
||||
"{backspace}": "⌫",
|
||||
"{capslock}": "caps lock ⇪",
|
||||
"{shift}": "⇧",
|
||||
"{controlleft}": "ctrl ⌃",
|
||||
"{controlright}": "ctrl ⌃",
|
||||
"{altleft}": "alt ⌥",
|
||||
"{altright}": "alt ⌥",
|
||||
"{metaleft}": "cmd ⌘",
|
||||
"{metaright}": "cmd ⌘",
|
||||
"{abc}": "ABC"
|
||||
}
|
||||
});
|
||||
this.makeKeyboardForLayout(this.layouts.default);
|
||||
}
|
||||
|
||||
unmount() {
|
||||
if (!this.keyboardDiv)
|
||||
if (!this.container)
|
||||
return; // Not mounted - don't do anything
|
||||
|
||||
this.keyboardDiv.parentElement.removeChild(this.keyboardDiv);
|
||||
this.keyboardDiv = null;
|
||||
}
|
||||
|
||||
onKeyPress(button) {
|
||||
if (button === "{shift}" || button === "{lock}")
|
||||
return this.handleShift();
|
||||
if (button === "{numbers}" || button === "{abc}")
|
||||
return this.handleNumbers();
|
||||
|
||||
this._sendKeyPress(button);
|
||||
}
|
||||
|
||||
handleShift() {
|
||||
this.keyboard.setOptions({
|
||||
layoutName: this.keyboard.options.layoutName === "default" ? "shift" : "default"
|
||||
});
|
||||
}
|
||||
|
||||
handleNumbers() {
|
||||
this.keyboard.setOptions({
|
||||
layoutName: this.keyboard.options.layoutName !== "numbers" ? "numbers" : "default"
|
||||
});
|
||||
this.container.parentElement.removeChild(this.container);
|
||||
this.container = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ class LoginPrompt {
|
|||
return; // Already mounted
|
||||
|
||||
this.element = document.createRange().createContextualFragment(`
|
||||
<div class="card small-card center-text">
|
||||
<div class="Card Card-ui-bottom Card-centered-text">
|
||||
<h2>Login</h2>
|
||||
<p>You need to enter the login code before you can start controlling your device.</p>
|
||||
<br>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const HOLDING_THRESHOLD_MS = 300;
|
||||
const SCROLL_X_DAMPENING = 0.03;
|
||||
const SCROLL_X_DAMPENING = 0.02;
|
||||
const SCROLL_Y_DAMPENING = 0.09;
|
||||
|
||||
class TouchpadController {
|
||||
|
@ -65,25 +65,26 @@ class TouchpadController {
|
|||
return; // Already mounted
|
||||
|
||||
this.touchpadDiv = document.createElement("div");
|
||||
this.touchpadDiv.classList.add("touchpad");
|
||||
element.appendChild(this.touchpadDiv);
|
||||
this.touchpadDiv.classList.add("Touchpad");
|
||||
|
||||
this.touchpadDiv.addEventListener("touchmove", this.onTouchMove.bind(this));
|
||||
this.touchpadDiv.addEventListener("touchend", this.onTouchEnd.bind(this));
|
||||
this.touchpadDiv.addEventListener("touchstart", this.onTouchStart.bind(this));
|
||||
|
||||
element.appendChild(this.touchpadDiv);
|
||||
}
|
||||
|
||||
unmount() {
|
||||
if (!this.touchpadDiv)
|
||||
return; // Not mounted - don't do anything
|
||||
|
||||
this.touchpadDiv.parentElement.removeChild(this.touchpadDiv);
|
||||
this.touchpadDiv = null;
|
||||
this.touchpadDiv.removeEventListener("touchmove", this.onTouchMove.bind(this));
|
||||
this.touchpadDiv.removeEventListener("touchend", this.onTouchEnd.bind(this));
|
||||
this.touchpadDiv.removeEventListener("touchstart", this.onTouchStart.bind(this));
|
||||
}
|
||||
|
||||
onTouchMove(event) {
|
||||
const touches = event.changedTouches;
|
||||
event.preventDefault();
|
||||
|
||||
const targetTouch = touches[0];
|
||||
|
||||
|
@ -125,7 +126,13 @@ class TouchpadController {
|
|||
}
|
||||
if (shouldMarkTouchesAsMoved) {
|
||||
for (let i = 0; i < touches.length; i++) {
|
||||
this.ongoingTouches[touches[i].identifier].hasMoved = true;
|
||||
const touch = this.ongoingTouches[touches[i].identifier];
|
||||
touch.hasMoved = true;
|
||||
|
||||
if (touch.indicatorElement) {
|
||||
touch.indicatorElement.style.top = `${targetTouch.pageY}px`;
|
||||
touch.indicatorElement.style.left = `${targetTouch.pageX}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,6 +157,14 @@ class TouchpadController {
|
|||
// remove all ended touches
|
||||
for (let i = 0; i < changedTouches.length; i++) {
|
||||
const touch = changedTouches[i];
|
||||
const knownTouch = this.ongoingTouches[changedTouches[0].identifier];
|
||||
|
||||
if (knownTouch && knownTouch.indicatorElement) {
|
||||
this.touchpadDiv.removeChild(knownTouch.indicatorElement);
|
||||
knownTouch.indicatorElement = null;
|
||||
delete knownTouch.indicatorElement;
|
||||
}
|
||||
|
||||
this.ongoingTouches[touch.identifier] = null;
|
||||
delete this.ongoingTouches[touch.identifier];
|
||||
}
|
||||
|
@ -177,12 +192,21 @@ class TouchpadController {
|
|||
|
||||
for (let i = 0; i < changedTouches.length; i++) {
|
||||
const touch = changedTouches[i];
|
||||
let indicatorElement = this.ongoingTouches[touch.identifier] ? this.ongoingTouches[touch.identifier].indicatorElement : null;
|
||||
if (!indicatorElement) {
|
||||
indicatorElement = document.createElement("div");
|
||||
indicatorElement.classList.add("Touchpad-touch-indicator");
|
||||
indicatorElement.style.top = `${touch.clientY}px`;
|
||||
indicatorElement.style.left = `${touch.clientX}px`;
|
||||
this.touchpadDiv.appendChild(indicatorElement);
|
||||
}
|
||||
this.ongoingTouches[touch.identifier] = {
|
||||
identifier: touch.identifier,
|
||||
clientX: touch.clientX,
|
||||
clientY: touch.clientY,
|
||||
hasMoved: false,
|
||||
gestured: []
|
||||
gestured: [],
|
||||
indicatorElement
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,4 +2,4 @@ import "simple-keyboard/build/css/index.css";
|
|||
import "./styles/main.css";
|
||||
import App from "./App";
|
||||
|
||||
new App(document.body);
|
||||
window.__app = new App(document.body);
|
||||
|
|
|
@ -1,29 +1,55 @@
|
|||
:root {
|
||||
--body-bg-color: #2e2e2e;
|
||||
--body-bg-color-accent: hsl(0, 0%, 25%);
|
||||
--accent-bg-color: #d4d3d3;
|
||||
--accent-color: #949494;
|
||||
--grayed-text-color: #949494;
|
||||
--button-accent-color: #3d3d3d;
|
||||
--selected-bg-color: #2e2e2e;
|
||||
--hover-bg-color: #4d4d4d;
|
||||
--card-border-radius: 1rem;
|
||||
--button-border-radius: 0.5rem;
|
||||
--card-border-radius: 1em;
|
||||
--button-border-radius: 0.5em;
|
||||
--main-font-weight: 500;
|
||||
|
||||
--fonts-regular: "Noto Sans", "Liberation Sans", sans-serif;
|
||||
}
|
||||
|
||||
body {
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html, body {
|
||||
font-weight: var(--main-font-weight);
|
||||
font-family: var(--fonts-regular);
|
||||
background-color: var(--body-bg-color);
|
||||
color: var(--accent-bg-color);
|
||||
overflow-x: none;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
padding: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.card {
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
background: var(--body-bg-color);
|
||||
background-image: radial-gradient(var(--body-bg-color-accent) 1.4px, transparent 0);
|
||||
background-size: 35px 35px;
|
||||
}
|
||||
|
||||
/* util */
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Card */
|
||||
|
||||
.Keyboard,
|
||||
.Card {
|
||||
margin: 6px;
|
||||
padding: 8px;
|
||||
background: var(--accent-bg-color);
|
||||
|
@ -31,56 +57,32 @@ body {
|
|||
border-radius: var(--card-border-radius);
|
||||
}
|
||||
|
||||
.card.inner-card {
|
||||
margin: 4px;
|
||||
margin-bottom: 28px;
|
||||
padding: 18px;
|
||||
border: solid var(--accent-color) 1px;
|
||||
}
|
||||
|
||||
.card.layout-card {
|
||||
margin: 36px auto;
|
||||
padding: 26px;
|
||||
width: 80%;
|
||||
.Keyboard,
|
||||
.Card-ui-bottom {
|
||||
padding: 14px;
|
||||
width: 100%;
|
||||
max-width: 610px;
|
||||
box-shadow: 0 0 28px 3px rgba(0, 0, 0, 0.40);
|
||||
margin: 0;
|
||||
border-radius: var(--card-border-radius) var(--card-border-radius) 0 0;
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.card.small-card {
|
||||
margin: 36px auto;
|
||||
padding: 26px;
|
||||
width: 350px;
|
||||
box-shadow: 0 0 28px 3px rgba(0, 0, 0, 0.40);
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.card.small-card {
|
||||
width: 80%;
|
||||
}
|
||||
}
|
||||
|
||||
.button-default {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
margin: 4px;
|
||||
text-decoration: none;
|
||||
border: none;
|
||||
background-color: var(--accent-bg-color);
|
||||
border-radius: var(--button-border-radius);
|
||||
font-size: 18px;
|
||||
white-space: nowrap;
|
||||
color: var(--button-accent-color);
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
min-width: 50px;
|
||||
.Card-centered-text {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
/* button */
|
||||
|
||||
.center-text {
|
||||
.button-default {
|
||||
display: block;
|
||||
padding: 12px;
|
||||
margin: 4px;
|
||||
border: none;
|
||||
background-color: var(--accent-bg-color);
|
||||
border-radius: var(--button-border-radius);
|
||||
color: var(--button-accent-color);
|
||||
min-width: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
@ -94,6 +96,8 @@ body {
|
|||
background-color: var(--selected-bg-color);
|
||||
}
|
||||
|
||||
/* input */
|
||||
|
||||
.input {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
|
@ -104,36 +108,51 @@ body {
|
|||
padding: 12px;
|
||||
}
|
||||
|
||||
.touchpad {
|
||||
height: clamp(5rem, 30rem, 50vh);
|
||||
/* Touchpad */
|
||||
|
||||
.Touchpad {
|
||||
width: 100%;
|
||||
background-color: var(--accent-bg-color);
|
||||
border-radius: var(--card-border-radius);
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* for virtual keyboard */
|
||||
.Touchpad-touch-indicator {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--accent-bg-color);
|
||||
}
|
||||
|
||||
.hg-theme-default {
|
||||
font-weight: var(--main-font-weight);
|
||||
/* Keyboard */
|
||||
|
||||
.keyboard-rows {
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.Keyboard-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.Keyboard-button {
|
||||
font-family: var(--fonts-regular);
|
||||
}
|
||||
|
||||
.keyboard {
|
||||
font-weight: var(--main-font-weight);
|
||||
padding: 8px;
|
||||
color: var(--body-bg-color);
|
||||
margin-top: 24px;
|
||||
border-radius: var(--card-border-radius);
|
||||
background-color: var(--accent-bg-color);
|
||||
color: #000000;
|
||||
flex-grow: 1;
|
||||
min-height: 50px;
|
||||
width: 20px;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.keyboard .hg-rows {
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
.hg-button {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.hg-activeButton {
|
||||
background: var(--accent-color) !important;
|
||||
.Keyboard-button:active {
|
||||
background-color: var(--accent-color);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,11 @@ module.exports = {
|
|||
plugins: [
|
||||
new HtmlWebpackPlugin({
|
||||
title: "Capybara",
|
||||
filename: "app.html"
|
||||
filename: "app.html",
|
||||
meta: {
|
||||
"viewport": "width=device-width,height=device-height,initial-scale=1,minimum-scale=1,user-scalable=no,viewport-fit=contain",
|
||||
"application-name": "Capybara"
|
||||
}
|
||||
}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: "[name].[contenthash].css"
|
||||
|
|
|
@ -1345,11 +1345,6 @@ signal-exit@^3.0.3:
|
|||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f"
|
||||
integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==
|
||||
|
||||
simple-keyboard@^3.3.16:
|
||||
version "3.3.16"
|
||||
resolved "https://registry.yarnpkg.com/simple-keyboard/-/simple-keyboard-3.3.16.tgz#045da7c291d8e973f489d9a686f5872fe8121475"
|
||||
integrity sha512-fAUNBIeNxh7Rlx3q11Sonq6DLFKYlfQJuRaiqlJkX9vYQeXEqdZGEh/FYyRUMIP0bjVVTI7Slq2H40sQe/DADQ==
|
||||
|
||||
source-map-js@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
|
||||
|
|
Loading…
Reference in a new issue