mirror of
https://github.com/hodgef/simple-keyboard.git
synced 2026-08-02 00:00:07 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8e8b6076a | ||
|
|
7c292d04bf | ||
|
|
f91e50a1b2 | ||
|
|
c7eb4ba4d3 | ||
|
|
d407413cce | ||
|
|
f8ebb30d76 | ||
|
|
276a478eb6 | ||
|
|
c239e21625 | ||
|
|
d42036ec3e | ||
|
|
cbd1506f53 | ||
|
|
d477c35cce | ||
|
|
4362e486a5 | ||
|
|
2502bcc910 | ||
|
|
df42db3b95 |
@@ -4,7 +4,6 @@
|
||||
/node_modules
|
||||
|
||||
# production
|
||||
/build
|
||||
/demo
|
||||
|
||||
# testing
|
||||
|
||||
@@ -1,57 +1,103 @@
|
||||
# simple-keyboard
|
||||
|
||||
[](https://www.npmjs.com/package/simple-keyboard)
|
||||
[](https://www.npmjs.com/package/simple-keyboard)
|
||||
[](https://www.npmjs.com/package/simple-keyboard)
|
||||
[](https://www.npmjs.com/package/simple-keyboard)
|
||||
|
||||
[](https://npmjs.com/package/simple-keyboard)
|
||||
<a href="https://franciscohodge.com/projects/simple-keyboard/"><img src="src/demo/images/simple-keyboard.png" align="center"></a>
|
||||
> An easily customisable and responsive on-screen virtual keyboard for Javascript projects.
|
||||
|
||||
> An easily customisable and responsive on-screen virtual keyboard for React.js projects.
|
||||
> Want the React.js version? Get [react-simple-keyboard](https://www.npmjs.com/package/react-simple-keyboard) instead!
|
||||
|
||||
<img src="src/demo/images/keyboard.PNG" align="center" width="100%">
|
||||
|
||||
<b>[Live Demo](https://franciscohodge.com/simple-keyboard/demo)</b>
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
### npm
|
||||
|
||||
`npm install simple-keyboard --save`
|
||||
|
||||
## Usage
|
||||
### zip file (self-hosted)
|
||||
|
||||
[Click here to download the latest release (zip format).](https://github.com/hodgef/simple-keyboard/zipball/master)
|
||||
|
||||
> Want to use a CDN instead of self-host? Scroll down to the "Usage from CDN" instructions below.
|
||||
|
||||
## Usage with npm
|
||||
|
||||
### js
|
||||
|
||||
````js
|
||||
import React, {Component} from 'react';
|
||||
import Keyboard from 'simple-keyboard';
|
||||
import 'simple-keyboard/build/css/index.css';
|
||||
|
||||
class App extends Component {
|
||||
class App {
|
||||
constructor(){
|
||||
document.addEventListener('DOMContentLoaded', this.onDOMLoaded);
|
||||
}
|
||||
|
||||
onChange = (input) => {
|
||||
onDOMLoaded = () => {
|
||||
this.keyboard = new Keyboard({
|
||||
onChange: input => this.onChange(input),
|
||||
onKeyPress: button => this.onKeyPress(button)
|
||||
});
|
||||
}
|
||||
|
||||
onChange = input => {
|
||||
console.log("Input changed", input);
|
||||
}
|
||||
|
||||
onKeyPress = (button) => {
|
||||
onKeyPress = button => {
|
||||
console.log("Button pressed", button);
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<Keyboard
|
||||
onChange={input =>
|
||||
this.onChange(input)}
|
||||
onKeyPress={button =>
|
||||
this.onKeyPress(button)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
````
|
||||
|
||||
### html
|
||||
|
||||
````html
|
||||
<div class="simple-keyboard"></div>
|
||||
````
|
||||
|
||||
> Need a more extensive example? [Click here](https://github.com/hodgef/simple-keyboard/blob/master/src/demo/App.js).
|
||||
|
||||
## Usage from CDN
|
||||
|
||||
### html
|
||||
|
||||
````html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<link rel="stylesheet" href="https://cdn.rawgit.com/hodgef/simple-keyboard/d477c35c/build/css/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="simple-keyboard"></div>
|
||||
<script src="https://cdn.rawgit.com/hodgef/simple-keyboard/d477c35c/build/index.js"></script>
|
||||
<script>
|
||||
function keyboard_onChange(input){
|
||||
console.log("Input changed", input);
|
||||
}
|
||||
|
||||
function keyboard_onKeyPress(button){
|
||||
console.log("Button pressed", button);
|
||||
}
|
||||
|
||||
var myKeyboard = new Keyboard({
|
||||
onChange: input => keyboard_onChange(input),
|
||||
onKeyPress: button => keyboard_onKeyPress(button)
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
````
|
||||
|
||||
## Options
|
||||
|
||||
You can customize the Keyboard by passing options (props) to it.
|
||||
You can customize the Keyboard by passing options to it.
|
||||
Here are the available options (the code examples are the defaults):
|
||||
|
||||
### layout
|
||||
@@ -59,7 +105,7 @@ Here are the available options (the code examples are the defaults):
|
||||
> Modify the keyboard layout
|
||||
|
||||
```js
|
||||
layout={{
|
||||
layout: {
|
||||
'default': [
|
||||
'` 1 2 3 4 5 6 7 8 9 0 - = {bksp}',
|
||||
'{tab} q w e r t y u i o p [ ] \\',
|
||||
@@ -74,7 +120,7 @@ layout={{
|
||||
'{shift} Z X C V B N M < > ? {shift}',
|
||||
'.com @ {space}'
|
||||
]
|
||||
}}
|
||||
}
|
||||
```
|
||||
|
||||
### layoutName
|
||||
@@ -82,7 +128,7 @@ layout={{
|
||||
> Specifies which layout should be used.
|
||||
|
||||
```js
|
||||
layoutName={"default"}
|
||||
layoutName: "default"
|
||||
```
|
||||
|
||||
### display
|
||||
@@ -90,7 +136,7 @@ layoutName={"default"}
|
||||
> Replaces variable buttons (such as `{bksp}`) with a human-friendly name (e.g.: "delete").
|
||||
|
||||
```js
|
||||
display={{
|
||||
display: {
|
||||
'{bksp}': 'delete',
|
||||
'{enter}': '< enter',
|
||||
'{shift}': 'shift',
|
||||
@@ -100,7 +146,7 @@ display={{
|
||||
'{accept}': 'Submit',
|
||||
'{space}': ' ',
|
||||
'{//}': ' '
|
||||
}}
|
||||
}
|
||||
```
|
||||
|
||||
### theme
|
||||
@@ -108,7 +154,7 @@ display={{
|
||||
> A prop to add your own css classes. You can add multiple classes separated by a space.
|
||||
|
||||
```js
|
||||
theme={"hg-theme-default"}
|
||||
theme: "hg-theme-default"
|
||||
```
|
||||
|
||||
### debug
|
||||
@@ -116,7 +162,7 @@ theme={"hg-theme-default"}
|
||||
> Runs a console.log every time a key is pressed. Displays the buttons pressed and the current input.
|
||||
|
||||
```js
|
||||
debug={false}
|
||||
debug: false
|
||||
```
|
||||
|
||||
### newLineOnEnter
|
||||
@@ -124,22 +170,30 @@ debug={false}
|
||||
> Specifies whether clicking the "ENTER" button will input a newline (`\n`) or not.
|
||||
|
||||
```js
|
||||
newLineOnEnter={true}
|
||||
newLineOnEnter: false
|
||||
```
|
||||
|
||||
### inputName
|
||||
|
||||
> Allows you to use a single simple-keyboard instance for several inputs.
|
||||
|
||||
```js
|
||||
inputName: "default"
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
simple-keybord has a few methods you can use to further control it's behavior.
|
||||
To access these functions, you need a `ref` of the simple-keyboard component, like so:
|
||||
simple-keyboard has a few methods you can use to further control it's behavior.
|
||||
To access these functions, you need the instance the simple-keyboard component, like so:
|
||||
|
||||
```js
|
||||
<Keyboard
|
||||
ref={r => this.keyboard = r}
|
||||
[...]
|
||||
var keyboard = new Keyboard({
|
||||
...
|
||||
});
|
||||
/>
|
||||
|
||||
// Then, on componentDidMount ..
|
||||
this.keyboard.methodName(params);
|
||||
// Then, use as follows...
|
||||
keyboard.methodName(params);
|
||||
```
|
||||
|
||||
### clearInput
|
||||
@@ -147,7 +201,7 @@ this.keyboard.methodName(params);
|
||||
> Clear the keyboard's input.
|
||||
|
||||
```js
|
||||
this.keyboard.clearInput();
|
||||
keyboard.clearInput();
|
||||
```
|
||||
|
||||
### getInput
|
||||
@@ -155,7 +209,7 @@ this.keyboard.clearInput();
|
||||
> Get the keyboard's input (You can also get it from the _onChange_ prop).
|
||||
|
||||
```js
|
||||
let input = this.keyboard.getInput();
|
||||
let input = keyboard.getInput();
|
||||
```
|
||||
|
||||
### setInput
|
||||
@@ -163,24 +217,61 @@ let input = this.keyboard.getInput();
|
||||
> Set the keyboard's input. Useful if you want the keybord to initialize with a default value, for example.
|
||||
|
||||
```js
|
||||
this.keyboard.setInput("Hello World!");
|
||||
keyboard.setInput("Hello World!");
|
||||
```
|
||||
|
||||
It returns a promise, so if you want to run something after it's applied, call it as so:
|
||||
### setOptions
|
||||
|
||||
> Set new option or modify existing ones after initialization. The changes are applied immediately.
|
||||
|
||||
```js
|
||||
let inputSetPromise = this.keyboard.setInput("Hello World!");
|
||||
|
||||
inputSetPromise.then((result) => {
|
||||
console.log("Input set");
|
||||
keyboard.setOptions({
|
||||
theme: "my-custom-theme"
|
||||
});
|
||||
```
|
||||
|
||||
## Use-cases
|
||||
|
||||
### Using several inputs
|
||||
|
||||
Set the *[inputName](#inputname)* option for each input you want to handle with simple-keyboard.
|
||||
|
||||
For example:
|
||||
```html
|
||||
<input class="input" id="input1" value=""/>
|
||||
<input class="input" id="input2" value=""/>
|
||||
```
|
||||
|
||||
```js
|
||||
// Initialize simple-keyboard as usual
|
||||
var keyboard = new Keyboard({
|
||||
onChange: input => console.log(input),
|
||||
onKeyPress: button => console.log(button),
|
||||
});
|
||||
|
||||
// Add an event listener for the inputs to be tracked
|
||||
document.querySelectorAll('.input')
|
||||
.forEach(input => input.addEventListener('focus', onInputFocus));
|
||||
|
||||
// Set the inputName option on the fly !
|
||||
function onInputFocus(event){
|
||||
keyboard.setOptions({
|
||||
inputName: event.target.id
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
[Using several inputs](#using-several-inputs)
|
||||
|
||||
## Demo
|
||||
|
||||
<img src="src/demo/images/demo.gif" align="center" width="600">
|
||||
|
||||
To run demo on your own computer:
|
||||
### Live demo
|
||||
|
||||
[https://franciscohodge.com/simple-keyboard/demo](https://franciscohodge.com/simple-keyboard/demo)
|
||||
|
||||
### To run demo on your own computer
|
||||
|
||||
* Clone this repository
|
||||
* `npm install`
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
body,html{margin:0;padding:0}.simple-keyboard{font-family:HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif;width:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden}.simple-keyboard .hg-row{display:-ms-flexbox;display:flex}.simple-keyboard .hg-row:not(:last-child){margin-bottom:5px}.simple-keyboard .hg-row .hg-button:not(:last-child){margin-right:5px}.simple-keyboard .hg-button{display:inline-block;-ms-flex-positive:1;flex-grow:1;cursor:pointer}.simple-keyboard.hg-layout-default .hg-button.hg-standardBtn{max-width:100px}.simple-keyboard.hg-theme-default{background-color:rgba(0,0,0,.2);padding:5px;border-radius:5px}.simple-keyboard.hg-theme-default .hg-button{-webkit-box-shadow:0 0 3px -1px rgba(0,0,0,.3);box-shadow:0 0 3px -1px rgba(0,0,0,.3);height:40px;border:1px solid rgba(0,0,0,.25);border-radius:5px;-webkit-box-sizing:border-box;box-sizing:border-box;padding:5px;background:#fff;border-bottom:1px solid gray}.simple-keyboard.hg-theme-default .hg-button:active{background:#e4e4e4}.simple-keyboard.hg-theme-default.hg-layout-numeric .hg-button{width:33.3%;height:60px;-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center}
|
||||
/*# sourceMappingURL=index.css.map*/
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["webpack:///./src/lib/components/Keyboard.css"],"names":[],"mappings":"AAAA,UACE,SACA,SAAW,CAGb,iBACE,6GACA,WACA,yBACG,sBACC,qBACI,iBACR,8BACQ,sBACR,eAAiB,CAGnB,yBACE,oBACA,YAAc,CAGhB,0CACE,iBAAmB,CAGrB,qDACE,gBAAkB,CAGpB,4BACE,qBACA,oBACI,YACJ,cAAgB,CAGlB,6DACE,eAAiB,CAMnB,kCACE,gCACA,YACA,iBAAmB,CAGrB,6CACE,+CACQ,uCACR,YACA,iCACA,kBACA,8BACQ,sBACR,YACA,gBACA,4BAA8B,CAG/B,oDACC,kBAAoB,CAGtB,+DACE,YACA,YACA,sBACI,mBACJ,oBACA,aACA,qBACI,sBAAwB","file":"css/index.css","sourcesContent":["body, html {\r\n margin: 0;\r\n padding: 0;\r\n}\r\n\r\n.simple-keyboard {\r\n font-family: \"HelveticaNeue-Light\", \"Helvetica Neue Light\", \"Helvetica Neue\", Helvetica, Arial, \"Lucida Grande\", sans-serif;\r\n width: 100%;\r\n -webkit-user-select: none;\r\n -moz-user-select: none;\r\n -ms-user-select: none;\r\n user-select: none;\r\n -webkit-box-sizing: border-box;\r\n box-sizing: border-box;\r\n overflow: hidden;\r\n}\r\n\r\n.simple-keyboard .hg-row {\r\n display: -ms-flexbox;\r\n display: flex;\r\n}\r\n\r\n.simple-keyboard .hg-row:not(:last-child) {\r\n margin-bottom: 5px;\r\n}\r\n\r\n.simple-keyboard .hg-row .hg-button:not(:last-child) {\r\n margin-right: 5px;\r\n}\r\n\r\n.simple-keyboard .hg-button {\r\n display: inline-block;\r\n -ms-flex-positive: 1;\r\n flex-grow: 1;\r\n cursor: pointer;\r\n}\r\n\r\n.simple-keyboard.hg-layout-default .hg-button.hg-standardBtn {\r\n max-width: 100px;\r\n}\r\n\r\n/**\r\n * hg-theme-default theme\r\n */\r\n.simple-keyboard.hg-theme-default {\r\n background-color: rgba(0,0,0,0.2);\r\n padding: 5px;\r\n border-radius: 5px;\r\n }\r\n\r\n.simple-keyboard.hg-theme-default .hg-button {\r\n -webkit-box-shadow: 0px 0px 3px -1px rgba(0,0,0,0.3);\r\n box-shadow: 0px 0px 3px -1px rgba(0,0,0,0.3);\r\n height: 40px;\r\n border: 1px solid rgba(0,0,0,0.25);\r\n border-radius: 5px;\r\n -webkit-box-sizing: border-box;\r\n box-sizing: border-box;\r\n padding: 5px;\r\n background: white;\r\n border-bottom: 1px solid gray;\r\n }\r\n\r\n .simple-keyboard.hg-theme-default .hg-button:active {\r\n background: #e4e4e4;\r\n }\r\n\r\n.simple-keyboard.hg-theme-default.hg-layout-numeric .hg-button {\r\n width: 33.3%;\r\n height: 60px;\r\n -ms-flex-align: center;\r\n align-items: center;\r\n display: -ms-flexbox;\r\n display: flex;\r\n -ms-flex-pack: center;\r\n justify-content: center;\r\n}\n\n\n// WEBPACK FOOTER //\n// ./src/lib/components/Keyboard.css"],"sourceRoot":""}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "simple-keyboard",
|
||||
"version": "0.0.1",
|
||||
"version": "2.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
+8
-7
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "simple-keyboard",
|
||||
"version": "1.1.2",
|
||||
"description": "React.js Virtual Keyboard",
|
||||
"version": "2.1.0",
|
||||
"description": "On-screen Virtual Keyboard",
|
||||
"main": "build/index.js",
|
||||
"scripts": {
|
||||
"start": "node scripts/start.js",
|
||||
@@ -18,17 +18,18 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/hodgef/simple-keyboard/issues"
|
||||
},
|
||||
"homepage": "https://github.com/hodgef/simple-keyboard",
|
||||
"homepage": "https://franciscohodge.com/simple-keyboard",
|
||||
"keywords": [
|
||||
"react",
|
||||
"reactjs",
|
||||
"javascript",
|
||||
"es6",
|
||||
"digital",
|
||||
"keyboard",
|
||||
"onscreen",
|
||||
"virtual",
|
||||
"component",
|
||||
"simple",
|
||||
"easy"
|
||||
"virtual-keyboard",
|
||||
"touchscreen",
|
||||
"touch-screen"
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {},
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 1.1 KiB |
+6
-2
@@ -19,13 +19,17 @@
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
<title>simple-keyboard</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
<div id="root"></div>
|
||||
<div id="root">
|
||||
<div class="simple-keyboard"></div>
|
||||
</div>
|
||||
|
||||
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
+41
-75
@@ -1,93 +1,59 @@
|
||||
import React, {Component} from 'react';
|
||||
import Keyboard from '../lib';
|
||||
import './App.css';
|
||||
import './css/App.css';
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
input: '',
|
||||
layoutName: "default"
|
||||
class App {
|
||||
constructor(){
|
||||
document.addEventListener('DOMContentLoaded', this.onDOMLoaded);
|
||||
|
||||
this.layoutName = "default";
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
this.keyboard.setInput("Hello World!")
|
||||
.then(input => {
|
||||
this.setState({input: input});
|
||||
});
|
||||
}
|
||||
|
||||
onChange = (input) => {
|
||||
this.setState({
|
||||
input: input
|
||||
}, () => {
|
||||
console.log("Input changed", input);
|
||||
onDOMLoaded = () => {
|
||||
this.keyboard = new Keyboard({
|
||||
debug: true,
|
||||
layoutName: this.layoutName,
|
||||
onChange: input => this.onChange(input),
|
||||
onKeyPress: button => this.onKeyPress(button),
|
||||
newLineOnEnter: true
|
||||
});
|
||||
}
|
||||
|
||||
onKeyPress = (button) => {
|
||||
console.log("Button pressed", button);
|
||||
|
||||
this.keyboard.setInput("Hello World!");
|
||||
|
||||
/**
|
||||
* Shift functionality
|
||||
* Adding preview (demo only)
|
||||
*/
|
||||
if(button === "{lock}" || button === "{shift}")
|
||||
this.handleShiftButton();
|
||||
|
||||
document.querySelector('.simple-keyboard').insertAdjacentHTML('beforebegin', `
|
||||
<div class="simple-keyboard-preview">
|
||||
<textarea class="input" readonly>Hello World!</textarea>
|
||||
</div>
|
||||
`);
|
||||
|
||||
console.log(this.keyboard);
|
||||
}
|
||||
|
||||
handleShiftButton = () => {
|
||||
let layoutName = this.state.layoutName;
|
||||
let shiftToggle = layoutName === "default" ? "shift" : "default";
|
||||
|
||||
this.setState({
|
||||
let layoutName = this.layoutName;
|
||||
let shiftToggle = this.layoutName = layoutName === "default" ? "shift" : "default";
|
||||
|
||||
this.keyboard.setOptions({
|
||||
layoutName: shiftToggle
|
||||
});
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<div className={"demoPage"}>
|
||||
<div className={"screenContainer"}>
|
||||
<textarea className={"inputContainer"} value={this.state.input} />
|
||||
</div>
|
||||
<Keyboard
|
||||
ref={r => this.keyboard = r}
|
||||
onChange={input => this.onChange(input)}
|
||||
onKeyPress={button => this.onKeyPress(button)}
|
||||
layoutName={this.state.layoutName}
|
||||
layout={{
|
||||
'default': [
|
||||
'` 1 2 3 4 5 6 7 8 9 0 - = {bksp}',
|
||||
'{tab} q w e r t y u i o p [ ] \\',
|
||||
'{lock} a s d f g h j k l ; \' {enter}',
|
||||
'{shift} z x c v b n m , . / {shift}',
|
||||
'.com @ {space}'
|
||||
],
|
||||
'shift': [
|
||||
'~ ! @ # $ % ^ & * ( ) _ + {bksp}',
|
||||
'{tab} Q W E R T Y U I O P { } |',
|
||||
'{lock} A S D F G H J K L : " {enter}',
|
||||
'{shift} Z X C V B N M < > ? {shift}',
|
||||
'.com @ {space}'
|
||||
]
|
||||
}}
|
||||
theme={"hg-layout-default hg-theme-default"}
|
||||
debug={true}
|
||||
display={{
|
||||
'{bksp}': 'delete',
|
||||
'{enter}': '< enter',
|
||||
'{shift}': 'shift',
|
||||
'{s}': 'shift',
|
||||
'{tab}': 'tab',
|
||||
'{lock}': 'caps',
|
||||
'{accept}': 'Submit',
|
||||
'{space}': ' ',
|
||||
'{//}': ' '
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
onChange = input => {
|
||||
document.querySelector('.input').value = input;
|
||||
}
|
||||
|
||||
|
||||
onKeyPress = button => {
|
||||
console.log("Button pressed", button);
|
||||
|
||||
/**
|
||||
* Shift functionality
|
||||
*/
|
||||
if(button === "{lock}" || button === "{shift}")
|
||||
this.handleShiftButton();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,77 @@
|
||||
import Keyboard from '../lib';
|
||||
import './css/MultipleInputsDemo.css';
|
||||
|
||||
class App {
|
||||
constructor(){
|
||||
document.addEventListener('DOMContentLoaded', this.onDOMLoaded);
|
||||
|
||||
this.layoutName = "default";
|
||||
}
|
||||
|
||||
onDOMLoaded = () => {
|
||||
this.keyboard = new Keyboard({
|
||||
debug: true,
|
||||
layoutName: this.layoutName,
|
||||
onChange: input => this.onChange(input),
|
||||
onKeyPress: button => this.onKeyPress(button)
|
||||
});
|
||||
|
||||
/**
|
||||
* Adding preview (demo only)
|
||||
* In production, this would be part of your HTML file
|
||||
*/
|
||||
document.querySelector('.simple-keyboard').insertAdjacentHTML('beforebegin', `
|
||||
<div>
|
||||
<label>Input 1</label>
|
||||
<input class="input" id="input1" value=""/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Input 2</label>
|
||||
<input class="input" id="input2" value=""/>
|
||||
</div>
|
||||
`);
|
||||
|
||||
/**
|
||||
* Changing active input onFocus
|
||||
*/
|
||||
document.querySelectorAll('.input')
|
||||
.forEach(input => input.addEventListener('focus', this.onInputFocus));
|
||||
|
||||
console.log(this.keyboard);
|
||||
}
|
||||
|
||||
onInputFocus = event => {
|
||||
this.selectedInput = `#${event.target.id}`;
|
||||
|
||||
this.keyboard.setOptions({
|
||||
inputName: event.target.id
|
||||
});
|
||||
}
|
||||
|
||||
onChange = input => {
|
||||
let currentInput = this.selectedInput || '.input';
|
||||
document.querySelector(currentInput).value = input;
|
||||
}
|
||||
|
||||
onKeyPress = button => {
|
||||
console.log("Button pressed", button);
|
||||
|
||||
/**
|
||||
* Shift functionality
|
||||
*/
|
||||
if(button === "{lock}" || button === "{shift}")
|
||||
this.handleShiftButton();
|
||||
}
|
||||
|
||||
handleShiftButton = () => {
|
||||
let layoutName = this.layoutName;
|
||||
let shiftToggle = this.layoutName = layoutName === "default" ? "shift" : "default";
|
||||
|
||||
this.keyboard.setOptions({
|
||||
layoutName: shiftToggle
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -1,11 +1,11 @@
|
||||
.demoPage {
|
||||
#root {
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.demoPage .screenContainer {
|
||||
#root .simple-keyboard-preview {
|
||||
background: rgba(0,0,0,0.8);
|
||||
border: 20px solid;
|
||||
height: 300px;
|
||||
@@ -15,7 +15,7 @@
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.demoPage .inputContainer {
|
||||
#root .input {
|
||||
color: white;
|
||||
background: transparent;
|
||||
border: none;
|
||||
@@ -0,0 +1,40 @@
|
||||
#root {
|
||||
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
#root .screenContainer {
|
||||
background: rgba(0,0,0,0.8);
|
||||
border: 20px solid;
|
||||
height: 300px;
|
||||
border-top-right-radius: 5px;
|
||||
border-top-left-radius: 5px;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#root .inputContainer {
|
||||
color: white;
|
||||
background: transparent;
|
||||
border: none;
|
||||
outline: none;
|
||||
font-family: monospace;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.simple-keyboard.hg-layout-custom {
|
||||
border-top-left-radius: 0px;
|
||||
border-top-right-radius: 0px;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
+4
-5
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
import registerServiceWorker from './registerServiceWorker';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
registerServiceWorker();
|
||||
/**
|
||||
* Initializing demo
|
||||
*/
|
||||
new App();
|
||||
@@ -1,108 +0,0 @@
|
||||
// In production, we register a service worker to serve assets from local cache.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on the "N+1" visit to a page, since previously
|
||||
// cached resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
|
||||
// This link also includes instructions on opting out of this behavior.
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
export default function register() {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Lets check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl);
|
||||
} else {
|
||||
// Is not local host. Just register service worker
|
||||
registerValidSW(swUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the old content will have been purged and
|
||||
// the fresh content will have been added to the cache.
|
||||
// It's the perfect time to display a "New content is
|
||||
// available; please refresh." message in your web app.
|
||||
console.log('New content is available; please refresh.');
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
if (
|
||||
response.status === 404 ||
|
||||
response.headers.get('content-type').indexOf('javascript') === -1
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
||||
+130
-94
@@ -1,53 +1,40 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import './Keyboard.css';
|
||||
|
||||
// Services
|
||||
import KeyboardLayout from '../services/KeyboardLayout';
|
||||
import Utilities from '../services/Utilities';
|
||||
|
||||
class App extends Component {
|
||||
state = {
|
||||
input: ''
|
||||
}
|
||||
class SimpleKeyboard {
|
||||
constructor(...params){
|
||||
let keyboardDOMQuery = typeof params[0] === "string" ? params[0] : '.simple-keyboard';
|
||||
let options = typeof params[0] === "object" ? params[0] : params[1];
|
||||
|
||||
componentWillReceiveProps = (nextProps) => {
|
||||
if(
|
||||
this.props !== nextProps
|
||||
){
|
||||
this.setState({
|
||||
layoutName: nextProps.layoutName,
|
||||
layout: nextProps.layout,
|
||||
themeClass: nextProps.theme
|
||||
});
|
||||
}
|
||||
}
|
||||
if(!options)
|
||||
options = {};
|
||||
|
||||
clearInput = () => {
|
||||
this.setState({
|
||||
input: ''
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Processing options
|
||||
*/
|
||||
this.keyboardDOM = document.querySelector(keyboardDOMQuery);
|
||||
this.options = options;
|
||||
this.options.layoutName = this.options.layoutName || "default";
|
||||
this.options.theme = this.options.theme || "hg-theme-default";
|
||||
this.options.inputName = this.options.inputName || "default";
|
||||
this.input = {};
|
||||
this.input[this.options.inputName] = '';
|
||||
|
||||
getInput = () => {
|
||||
return this.state.input;
|
||||
}
|
||||
|
||||
setInput = input => {
|
||||
return new Promise(resolve => {
|
||||
this.setState({
|
||||
input: input
|
||||
}, () => {
|
||||
resolve(input);
|
||||
});
|
||||
})
|
||||
.catch(reason => {
|
||||
console.warn(reason);
|
||||
});
|
||||
/**
|
||||
* Rendering keyboard
|
||||
*/
|
||||
if(this.keyboardDOM)
|
||||
this.render();
|
||||
else
|
||||
console.error(`"${keyboardDOMQuery}" was not found in the DOM.`);
|
||||
}
|
||||
|
||||
handleButtonClicked = (button) => {
|
||||
let debug = this.props.debug;
|
||||
let debug = this.options.debug;
|
||||
|
||||
/**
|
||||
* Ignoring placeholder buttons
|
||||
@@ -58,32 +45,32 @@ class App extends Component {
|
||||
/**
|
||||
* Calling onKeyPress
|
||||
*/
|
||||
if(typeof this.props.onKeyPress === "function")
|
||||
this.props.onKeyPress(button);
|
||||
if(typeof this.options.onKeyPress === "function")
|
||||
this.options.onKeyPress(button);
|
||||
|
||||
/**
|
||||
* Updating input
|
||||
*/
|
||||
let options = {
|
||||
newLineOnEnter: this.props.newLineOnEnter !== false || true
|
||||
newLineOnEnter: (this.options.newLineOnEnter === true)
|
||||
}
|
||||
|
||||
let updatedInput = Utilities.getUpdatedInput(button, this.state.input, options);
|
||||
if(!this.input[this.options.inputName])
|
||||
this.input[this.options.inputName] = '';
|
||||
|
||||
if(this.state.input !== updatedInput){
|
||||
this.setState({
|
||||
input: updatedInput
|
||||
}, () => {
|
||||
if(debug){
|
||||
console.log('Input changed:', this.state.input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling onChange
|
||||
*/
|
||||
if(typeof this.props.onChange === "function")
|
||||
this.props.onChange(this.state.input);
|
||||
});
|
||||
let updatedInput = Utilities.getUpdatedInput(button, this.input[this.options.inputName], options);
|
||||
|
||||
if(this.input[this.options.inputName] !== updatedInput){
|
||||
this.input[this.options.inputName] = updatedInput;
|
||||
|
||||
if(debug)
|
||||
console.log('Input changed:', this.input);
|
||||
|
||||
/**
|
||||
* Calling onChange
|
||||
*/
|
||||
if(typeof this.options.onChange === "function")
|
||||
this.options.onChange(this.input[this.options.inputName]);
|
||||
}
|
||||
|
||||
if(debug){
|
||||
@@ -91,48 +78,97 @@ class App extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let layoutName = this.props.layoutName || "default";
|
||||
let layout = this.props.layout || KeyboardLayout.getLayout(layoutName);
|
||||
let themeClass = this.props.theme || "hg-theme-default";
|
||||
let layoutClass = this.props.layout ? "hg-layout-custom" : `hg-layout-${layoutName}`;
|
||||
clearInput = (inputName) => {
|
||||
inputName = inputName || this.options.inputName;
|
||||
this.input[this.options.inputName] = '';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`simple-keyboard ${themeClass} ${layoutClass}`}>
|
||||
{layout[layoutName].map((row, index) => {
|
||||
let rowArray = row.split(' ');
|
||||
getInput = (inputName) => {
|
||||
inputName = inputName || this.options.inputName;
|
||||
return this.input[this.options.inputName];
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={`hg-row-${index}`} className="hg-row">
|
||||
{rowArray.map((button, index) => {
|
||||
let fctBtnClass = Utilities.getButtonClass(button);
|
||||
let buttonDisplayName = Utilities.getButtonDisplayName(button, this.props.display);
|
||||
setInput = (input, inputName) => {
|
||||
inputName = inputName || this.options.inputName;
|
||||
this.input[inputName] = input;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`hg-button-${index}`}
|
||||
className={`hg-button ${fctBtnClass}`}
|
||||
onClick={() => this.handleButtonClicked(button)}
|
||||
><span>{buttonDisplayName}</span></div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
setOptions = option => {
|
||||
option = option || {};
|
||||
this.options = Object.assign(this.options, option);
|
||||
this.render();
|
||||
}
|
||||
|
||||
clear = () => {
|
||||
this.keyboardDOM.innerHTML = '';
|
||||
}
|
||||
|
||||
render = () => {
|
||||
/**
|
||||
* Clear keyboard
|
||||
*/
|
||||
this.clear();
|
||||
|
||||
let layoutClass = this.options.layout ? "hg-layout-custom" : `hg-layout-${this.options.layoutName}`;
|
||||
let layout = this.options.layout || KeyboardLayout.getLayout(this.options.layoutName);
|
||||
|
||||
/**
|
||||
* Adding themeClass, layoutClass to keyboardDOM
|
||||
*/
|
||||
this.keyboardDOM.className += ` ${this.options.theme} ${layoutClass}`;
|
||||
|
||||
/**
|
||||
* Iterating through each row
|
||||
*/
|
||||
layout[this.options.layoutName].forEach((row, index) => {
|
||||
let rowArray = row.split(' ');
|
||||
|
||||
/**
|
||||
* Creating empty row
|
||||
*/
|
||||
var rowDOM = document.createElement('div');
|
||||
rowDOM.className += "hg-row";
|
||||
|
||||
/**
|
||||
* Iterating through each button in row
|
||||
*/
|
||||
rowArray.forEach((button, index) => {
|
||||
let fctBtnClass = Utilities.getButtonClass(button);
|
||||
let buttonDisplayName = Utilities.getButtonDisplayName(button, this.options.display);
|
||||
|
||||
/**
|
||||
* Creating button
|
||||
*/
|
||||
var buttonDOM = document.createElement('div');
|
||||
buttonDOM.className += `hg-button ${fctBtnClass}`;
|
||||
buttonDOM.onclick = () => this.handleButtonClicked(button);
|
||||
|
||||
/**
|
||||
* Adding button label to button
|
||||
*/
|
||||
var buttonSpanDOM = document.createElement('span');
|
||||
buttonSpanDOM.innerHTML = buttonDisplayName;
|
||||
buttonDOM.appendChild(buttonSpanDOM);
|
||||
|
||||
/**
|
||||
* Appending button to row
|
||||
*/
|
||||
rowDOM.appendChild(buttonDOM);
|
||||
|
||||
/**
|
||||
* Calling onInit
|
||||
*/
|
||||
if(typeof this.options.onInit === "function")
|
||||
this.options.onInit();
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Appending row to keyboard
|
||||
*/
|
||||
this.keyboardDOM.appendChild(rowDOM);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
App.propTypes = {
|
||||
layoutName: PropTypes.string,
|
||||
layout: PropTypes.object,
|
||||
theme: PropTypes.string,
|
||||
display: PropTypes.object,
|
||||
onChange: PropTypes.func,
|
||||
onKeyPress: PropTypes.func,
|
||||
debug: PropTypes.bool
|
||||
};
|
||||
|
||||
export default App;
|
||||
export default SimpleKeyboard;
|
||||
|
||||
+2
-2
@@ -1,2 +1,2 @@
|
||||
import Keyboard from './components/Keyboard';
|
||||
export default Keyboard;
|
||||
import SimpleKeyboard from './components/Keyboard';
|
||||
export default SimpleKeyboard;
|
||||
|
||||
Reference in New Issue
Block a user