This commit is contained in:
Francisco Hodge
2019-08-17 15:39:38 -04:00
20 changed files with 4170 additions and 1390 deletions
+73
View File
@@ -0,0 +1,73 @@
import Keyboard from "../lib";
import "./css/ButtonThemeDemo.css";
const setDOM = () => {
document.querySelector("#root").innerHTML = `
<input class="input" placeholder="Tap on the virtual keyboard to start" />
<div class="simple-keyboard"></div>
`;
};
class Demo {
constructor() {
setDOM();
/**
* Demo Start
*/
this.keyboard = new Keyboard({
theme: "hg-theme-default my-theme",
onChange: input => this.onChange(input),
onKeyPress: button => this.onKeyPress(button),
buttonTheme: [
{
class: "my-button",
buttons: "{enter} {bksp} q Q"
},
{
class: "my-button-outline",
buttons: "q Q b B"
}
],
buttonAttributes: [
{
attribute: "aria-label",
value: "bee",
buttons: "b B"
}
]
});
/**
* Update simple-keyboard when input is changed directly
*/
document.querySelector(".input").addEventListener("input", event => {
this.keyboard.setInput(event.target.value);
});
}
onChange(input) {
document.querySelector(".input").value = input;
console.log("Input changed", input);
}
onKeyPress(button) {
console.log("Button pressed", button);
/**
* If you want to handle the shift and caps lock buttons
*/
if (button === "{shift}" || button === "{lock}") this.handleShift();
}
handleShift() {
let currentLayout = this.keyboard.options.layoutName;
let shiftToggle = currentLayout === "default" ? "shift" : "default";
this.keyboard.setOptions({
layoutName: shiftToggle
});
}
}
export default Demo;
+26
View File
@@ -0,0 +1,26 @@
input {
width: 100%;
height: 100px;
padding: 20px;
font-size: 20px;
border: none;
box-sizing: border-box;
}
.simple-keyboard {
max-width: 850px;
}
.simple-keyboard.my-theme .hg-button.my-button {
background: blue;
color: white;
}
.simple-keyboard.my-theme .hg-button[aria-label] {
background: black;
color: white;
}
.simple-keyboard.my-theme .hg-button.my-button-outline {
border: 2px solid red;
}
+1
View File
@@ -5,6 +5,7 @@ import "./css/index.css";
*/
import BasicDemo from "./BasicDemo";
//import FullKeyboardDemo from "./FullKeyboardDemo";
//import ButtonThemeDemo from "./ButtonThemeDemo";
/**
* Selected demo
+77
View File
@@ -0,0 +1,77 @@
import TestUtility from '../../utils/TestUtility';
import ButtonThemeDemo from '../ButtonThemeDemo';
let testUtil = new TestUtility();
it('Demo will load', () => {
testUtil.setDOM();
let demo = new ButtonThemeDemo();
});
it('Demo onDOMLoaded will work', () => {
testUtil.setDOM();
let demo = new ButtonThemeDemo();
expect(demo.keyboard).toBeTruthy();
});
it('Demo onChange will work', () => {
testUtil.setDOM();
let demo = new ButtonThemeDemo();
demo.onChange("test");
expect(document.body.querySelector('.input').value).toBe("test");
});
it('Demo onChange will work', () => {
testUtil.setDOM();
let demo = new ButtonThemeDemo();
demo.keyboard.getButtonElement("q").onclick();
expect(document.body.querySelector('.input').value).toBe("q");
});
it('Demo input change will work', () => {
testUtil.setDOM();
let demo = new ButtonThemeDemo();
document.body.querySelector('.input').value = "test";
document.body.querySelector('.input').dispatchEvent(new Event('input'));
expect(demo.keyboard.getInput()).toBe("test");
});
it('Demo handleShiftButton will work', () => {
testUtil.setDOM();
let demo = new ButtonThemeDemo();
demo.keyboard.getButtonElement("{shift}")[0].onclick();
expect(demo.keyboard.options.layoutName).toBe("shift");
demo.keyboard.getButtonElement("{shift}")[0].onclick();
expect(demo.keyboard.options.layoutName).toBe("default");
});
it('Demo buttons will have proper attributes and classes', () => {
testUtil.setDOM();
let demo = new ButtonThemeDemo();
let buttonDOM = demo.keyboard.getButtonElement("b");
console.log("buttonDOM", buttonDOM.outerHTML);
let hasAttribute = buttonDOM.hasAttribute("aria-label");
expect(hasAttribute).toBeTruthy();
let hasClass = buttonDOM.classList.contains("my-button-outline");
expect(hasClass).toBeTruthy();
});
+20
View File
@@ -10,6 +10,12 @@ declare module 'simple-keyboard' {
buttons: string;
}
interface KeyboardButtonAttributes {
attribute: string;
value: string;
buttons: string;
}
interface KeyboardOptions {
/**
* Modify the keyboard layout.
@@ -41,6 +47,11 @@ declare module 'simple-keyboard' {
*/
buttonTheme?: KeyboardButtonTheme[];
/**
* A prop to add your own attributes to one or several buttons.
*/
buttonAttributes?: KeyboardButtonAttributes[];
/**
* Runs a `console.log` every time a key is pressed. Displays the buttons pressed and the current input.
*/
@@ -177,6 +188,10 @@ declare module 'simple-keyboard' {
class Keyboard {
constructor(selector: string, options: KeyboardOptions);
constructor(options: KeyboardOptions);
/**
* Options
*/
options: KeyboardOptions;
/**
@@ -184,6 +199,11 @@ declare module 'simple-keyboard' {
*/
utilities?: any;
/**
* caretPosition
*/
caretPosition?: number;
/**
* Adds/Modifies an entry to the `buttonTheme`. Basically a way to add a class to a button.
* @param {string} buttons List of buttons to select (separated by a space).
+64 -51
View File
@@ -51,6 +51,7 @@ class SimpleKeyboard {
* @property {boolean} mergeDisplay By default, when you set the display property, you replace the default one. This setting merges them instead.
* @property {string} theme A prop to add your own css classes to the keyboard wrapper. You can add multiple classes separated by a space.
* @property {Array} buttonTheme A prop to add your own css classes to one or several buttons.
* @property {Array} buttonAttributes A prop to add your own attributes to one or several buttons.
* @property {boolean} debug Runs a console.log every time a key is pressed. Displays the buttons pressed and the current input.
* @property {boolean} newLineOnEnter Specifies whether clicking the “ENTER” button will input a newline (\n) or not.
* @property {boolean} tabCharOnTab Specifies whether clicking the “TAB” button will input a tab character (\t) or not.
@@ -733,47 +734,62 @@ class SimpleKeyboard {
/**
* Process buttonTheme option
*/
getButtonTheme() {
let buttonThemesParsed = {};
getButtonThemeClasses(button) {
let buttonTheme = this.options.buttonTheme;
let buttonClasses = [];
this.options.buttonTheme.forEach(themeObj => {
if (themeObj.buttons && themeObj.class) {
let themeButtons;
if (Array.isArray(buttonTheme)) {
buttonTheme.forEach(themeObj => {
if (
themeObj.class &&
typeof themeObj.class === "string" &&
(themeObj.buttons && typeof themeObj.buttons === "string")
) {
let themeObjClasses = themeObj.class.split(" ");
let themeObjButtons = themeObj.buttons.split(" ");
if (typeof themeObj.buttons === "string") {
themeButtons = themeObj.buttons.split(" ");
if (themeObjButtons.includes(button)) {
buttonClasses = [...buttonClasses, ...themeObjClasses];
}
} else {
console.warn(
`Incorrect "buttonTheme". Please check the documentation.`,
themeObj
);
}
});
}
if (themeButtons) {
themeButtons.forEach(themeButton => {
let themeParsed = buttonThemesParsed[themeButton];
return buttonClasses;
}
// If the button has already been added
if (themeParsed) {
// Making sure we don't add duplicate classes, even when buttonTheme has duplicates
if (
!this.utilities.countInArray(
themeParsed.split(" "),
themeObj.class
)
) {
buttonThemesParsed[
themeButton
] = `${themeParsed} ${themeObj.class}`;
}
} else {
buttonThemesParsed[themeButton] = themeObj.class;
}
});
/**
* Process buttonAttributes option
*/
setDOMButtonAttributes(button, callback) {
let buttonAttributes = this.options.buttonAttributes;
if (Array.isArray(buttonAttributes)) {
buttonAttributes.forEach(attrObj => {
if (
attrObj.attribute &&
typeof attrObj.attribute === "string" &&
(attrObj.value && typeof attrObj.value === "string") &&
(attrObj.buttons && typeof attrObj.buttons === "string")
) {
let attrObjButtons = attrObj.buttons.split(" ");
if (attrObjButtons.includes(button)) {
callback(attrObj.attribute, attrObj.value);
}
} else {
console.warn(
`Incorrect "buttonAttributes". Please check the documentation.`,
attrObj
);
}
} else {
console.warn(
`buttonTheme row is missing the "buttons" or the "class". Please check the documentation.`
);
}
});
return buttonThemesParsed;
});
}
}
onTouchDeviceDetected() {
@@ -1061,13 +1077,6 @@ class SimpleKeyboard {
let useMouseEvents = this.options.useMouseEvents || false;
let disableRowButtonContainers = this.options.disableRowButtonContainers;
/**
* Account for buttonTheme, if set
*/
let buttonThemesParsed = Array.isArray(this.options.buttonTheme)
? this.getButtonTheme()
: {};
/**
* Adding themeClass, layoutClass to keyboardDOM
*/
@@ -1132,7 +1141,6 @@ class SimpleKeyboard {
* Processing button options
*/
let fctBtnClass = this.utilities.getButtonClass(button);
let buttonThemeClass = buttonThemesParsed[button];
let buttonDisplayName = this.utilities.getButtonDisplayName(
button,
this.options.display,
@@ -1144,9 +1152,19 @@ class SimpleKeyboard {
*/
let buttonType = this.options.useButtonTag ? "button" : "div";
let buttonDOM = document.createElement(buttonType);
buttonDOM.className += `hg-button ${fctBtnClass}${
buttonThemeClass ? " " + buttonThemeClass : ""
}`;
buttonDOM.className += `hg-button ${fctBtnClass}`;
/**
* Adding buttonTheme
*/
buttonDOM.classList.add(...this.getButtonThemeClasses(button));
/**
* Adding buttonAttributes
*/
this.setDOMButtonAttributes(button, (attribute, value) => {
buttonDOM.setAttribute(attribute, value);
});
/**
* Handle button click event
@@ -1205,11 +1223,6 @@ class SimpleKeyboard {
let buttonUID = `${this.options.layoutName}-r${rIndex}b${bIndex}`;
buttonDOM.setAttribute("data-skBtnUID", buttonUID);
/**
* Adding display label
*/
buttonDOM.setAttribute("data-displayLabel", buttonDisplayName);
/**
* Adding button label to button
*/
+27
View File
@@ -1298,4 +1298,31 @@ it('Keyboard onKeyReleased will work', () => {
expect(pressed).toBeTruthy();
expect(firedTimes).toBe(1);
expect(buttonPressed).toBe("q");
});
it('Keyboard buttonAttribute will work', () => {
testUtil.setDOM();
let keyboard = new Keyboard({
buttonAttributes: [
{
attribute: "aria-label",
value: "bee",
buttons: "b B"
}
]
});
});
it('Keyboard buttonAttribute will warn about invalid entries', () => {
testUtil.setDOM();
let keyboard = new Keyboard({
buttonAttributes: [
{
attribute: false,
value: null
}
]
});
});
-10
View File
@@ -401,16 +401,6 @@ class Utilities {
word.length ? string + word[0].toUpperCase() + word.slice(1) : string
);
}
/**
* Counts the number of duplicates in a given array
*
* @param {Array} array The haystack to search in
* @param {string} value The needle to search for
*/
countInArray(array, value) {
return array.reduce((n, x) => n + (x === value), 0);
}
}
export default Utilities;
+1 -1
View File
@@ -13,7 +13,7 @@ it('Keyboard mergeDisplay will work', () => {
}
});
expect(keyboard.getButtonElement("q").getAttribute("data-displaylabel")).toBe("qreplaced");
expect(keyboard.getButtonElement("q").querySelector("span").innerHTML).toBe("qreplaced");
});
it('Keyboard function buttons will work', () => {