Port to typescript

This commit is contained in:
Francisco Hodge
2021-03-07 02:52:17 -05:00
parent ae54359b40
commit 8ba81892fe
45 changed files with 5511 additions and 18599 deletions
-329
View File
@@ -1,329 +0,0 @@
declare module 'simple-keyboard' {
export interface KeyboardLayoutObject {
[key: string]: string[];
}
export interface KeyboardButtonTheme {
class: string;
buttons: string;
}
export interface KeyboardButtonAttributes {
attribute: string;
value: string;
buttons: string;
}
export interface KeyboardOptions {
/**
* Modify the keyboard layout.
*/
layout?: KeyboardLayoutObject;
/**
* Specifies which layout should be used.
*/
layoutName?: string;
/**
* Replaces variable buttons (such as `{bksp}`) with a human-friendly name (e.g.: `backspace`).
*/
display?: { [button: string]: string };
/**
* By default, when you set the display property, you replace the default one. This setting merges them instead.
*/
mergeDisplay?: boolean;
/**
* A prop to add your own css classes to the keyboard wrapper. You can add multiple classes separated by a space.
*/
theme?: string;
/**
* A prop to add your own css classes to one or several buttons.
*/
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.
*/
debug?: boolean;
/**
* Specifies whether clicking the "ENTER" button will input a newline (`\n`) or not.
*/
newLineOnEnter?: boolean;
/**
* Specifies whether clicking the "TAB" button will input a tab character (`\t`) or not.
*/
tabCharOnTab?: boolean;
/**
* Allows you to use a single simple-keyboard instance for several inputs.
*/
inputName?: string;
/**
* `number`: Restrains all of simple-keyboard inputs to a certain length. This should be used in addition to the input elements maxlengthattribute.
*
* `{ [inputName: string]: number }`: Restrains simple-keyboards individual inputs to a certain length. This should be used in addition to the input elements maxlengthattribute.
*/
maxLength?: any;
/**
* When set to true, this option synchronizes the internal input of every simple-keyboard instance.
*/
syncInstanceInputs?: boolean;
/**
* Enable highlighting of keys pressed on physical keyboard.
*/
physicalKeyboardHighlight?: boolean;
/**
* Presses keys highlighted by physicalKeyboardHighlight
*/
physicalKeyboardHighlightPress?: boolean;
/**
* Define the text color that the physical keyboard highlighted key should have.
*/
physicalKeyboardHighlightTextColor?: string;
/**
* Define the background color that the physical keyboard highlighted key should have.
*/
physicalKeyboardHighlightBgColor?: string;
/**
* Calling preventDefault for the mousedown events keeps the focus on the input.
*/
preventMouseDownDefault?: boolean;
/**
* Calling preventDefault for the mouseup events.
*/
preventMouseUpDefault?: boolean;
/**
* Stops pointer down events on simple-keyboard buttons from bubbling to parent elements.
*/
stopMouseDownPropagation?: boolean;
/**
* Stops pointer up events on simple-keyboard buttons from bubbling to parent elements.
*/
stopMouseUpPropagation?: boolean;
/**
* Render buttons as a button element instead of a div element.
*/
useButtonTag?: boolean;
/**
* A prop to ensure characters are always be added/removed at the end of the string.
*/
disableCaretPositioning?: boolean;
/**
* Restrains input(s) change to the defined regular expression pattern.
*/
inputPattern?: any;
/**
* Instructs simple-keyboard to use touch events instead of click events.
*/
useTouchEvents?: boolean;
/**
* Enable useTouchEvents automatically when touch device is detected.
*/
autoUseTouchEvents?: boolean;
/**
* Opt out of PointerEvents handling, falling back to the prior mouse event logic.
*/
useMouseEvents?: boolean;
/**
* Disable button hold action.
*/
disableButtonHold?: boolean;
/**
* Adds unicode right-to-left control characters to input return values.
*/
rtl?: boolean;
/**
* Executes the callback function on key press. Returns button layout name (i.e.: "{shift}").
*/
onKeyPress?: (button: string) => any;
/**
* Executes the callback function on key release.
*/
onKeyReleased?: (button: string) => any;
/**
* Executes the callback function on input change. Returns the current input's string.
*/
onChange?: (input: string) => any;
/**
* Executes the callback function before the first simple-keyboard render.
*/
beforeFirstRender?: () => void;
/**
* Executes the callback function before a simple-keyboard render.
*/
beforeRender?: () => void;
/**
* Executes the callback function every time simple-keyboard is rendered (e.g: when you change layouts).
*/
onRender?: () => void;
/**
* Executes the callback function once simple-keyboard is rendered for the first time (on initialization).
*/
onInit?: () => void;
/**
* Executes the callback function on input change. Returns the input object with all defined inputs.
*/
onChangeAll?: (inputs: any) => any;
/**
* Module classes to be loaded by simple-keyboard.
*/
modules?: any[];
/**
* Module options can have any format
*/
[name: string]: any;
}
class Keyboard {
constructor(selector: string, options: KeyboardOptions);
constructor(selector: HTMLDivElement, options: KeyboardOptions);
constructor(options: KeyboardOptions);
/**
* Options
*/
options: KeyboardOptions;
/**
* Utilities
*/
utilities?: any;
/**
* caretPosition
*/
caretPosition?: number;
/**
* caretPositionEnd
*/
caretPositionEnd?: number;
/**
* Changes the internal caret position
* @param {number} position The caret's start position
* @param {number} positionEnd The caret's end position
*/
setCaretPosition(position: number, positionEnd?: number): void;
/**
* Retrieves the internal caret position
*/
getCaretPosition(): number;
/**
* Retrieves the internal end caret position
*/
getCaretPositionEnd(): 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).
* @param {string} className Classes to give to the selected buttons (separated by space).
*/
addButtonTheme(buttons: string, className: string): void;
/**
* Removes/Amends an entry to the `buttonTheme`. Basically a way to remove a class previously added to a button through buttonTheme or addButtonTheme.
* @param {string} buttons List of buttons to select (separated by a space).
* @param {string} className Classes to give to the selected buttons (separated by space).
*/
removeButtonTheme(buttons: string, className: string): void;
/**
* Clear the keyboard's input.
*
* @param {string} [inputName] optional - the internal input to select
*/
clearInput(inputName?: string): void;
/**
* Get the keyboards input (You can also get it from the onChange prop).
* @param {string} [inputName] optional - the internal input to select
*/
getInput(inputName?: string): string;
/**
* Set the keyboards input.
* @param {string} input the input value
* @param {string} inputName optional - the internal input to select
*/
setInput(input: string, inputName?: string): void;
/**
* Replaces the entire internal input object.
* @param {string} input the input object
*/
replaceInput(input: any): void;
/**
* Set new option or modify existing ones after initialization.
* @param {KeyboardOptions} option The option to set
*/
setOptions(options: KeyboardOptions): void;
/**
* Send a command to all simple-keyboard instances at once (if you have multiple instances).
* @param {function(instance: object, key: string)} callback Function to run on every instance
*/
dispatch(callback: (instance: any, key: string) => void): void;
/**
* Get the DOM Element of a button. If there are several buttons with the same name, an array of the DOM Elements is returned.
* @param {string} button The button layout name to select
*/
getButtonElement(button: string): HTMLElement | HTMLElement[];
/**
* Clears keyboard listeners and DOM elements.
*/
destroy(): void;
/**
* Iterate on each button (ForEach)
*/
recurseButtons(fn: any): void;
}
export default Keyboard;
}
@@ -4,6 +4,13 @@ import "./Keyboard.css";
import { getDefaultLayout } from "../services/KeyboardLayout";
import PhysicalKeyboard from "../services/PhysicalKeyboard";
import Utilities from "../services/Utilities";
import {
KeyboardOptions,
KeyboardInput,
KeyboardButtonElements,
KeyboardHandlerEvent,
KeyboardButton,
} from "../interfaces";
/**
* Root class for simple-keyboard
@@ -13,11 +20,32 @@ import Utilities from "../services/Utilities";
* - Handles button functionality
*/
class SimpleKeyboard {
input: KeyboardInput;
options: KeyboardOptions;
utilities: any;
caretPosition: number;
caretPositionEnd: number;
keyboardDOM: KeyboardButton;
keyboardPluginClasses: string;
keyboardDOMClass: string;
buttonElements: KeyboardButtonElements;
currentInstanceName: string;
allKeyboardInstances: { [key: string]: SimpleKeyboard };
keyboardInstanceNames: string[];
isFirstKeyboardInstance: boolean;
physicalKeyboard: PhysicalKeyboard;
modules: { [key: string]: any };
activeButtonClass: string;
holdInteractionTimeout: number;
holdTimeout: number;
isMouseHold: boolean;
initialized: boolean;
/**
* Creates an instance of SimpleKeyboard
* @param {Array} params If first parameter is a string, it is considered the container class. The second parameter is then considered the options object. If first parameter is an object, it is considered the options object.
*/
constructor(...params) {
constructor(...params: []) {
const { keyboardDOMClass, keyboardDOM, options = {} } = this.handleParams(
params
);
@@ -29,7 +57,7 @@ class SimpleKeyboard {
getOptions: this.getOptions,
getCaretPosition: this.getCaretPosition,
getCaretPositionEnd: this.getCaretPositionEnd,
dispatch: this.dispatch
dispatch: this.dispatch,
});
/**
@@ -155,7 +183,7 @@ class SimpleKeyboard {
*/
this.physicalKeyboard = new PhysicalKeyboard({
dispatch: this.dispatch,
getOptions: this.getOptions
getOptions: this.getOptions,
});
/**
@@ -177,7 +205,13 @@ class SimpleKeyboard {
/**
* parseParams
*/
handleParams = params => {
handleParams = (
params: any[]
): {
keyboardDOMClass: string;
keyboardDOM: KeyboardButton;
options: Partial<KeyboardOptions>;
} => {
let keyboardDOMClass;
let keyboardDOM;
let options;
@@ -188,11 +222,13 @@ class SimpleKeyboard {
*/
if (typeof params[0] === "string") {
keyboardDOMClass = params[0].split(".").join("");
keyboardDOM = document.querySelector(`.${keyboardDOMClass}`);
keyboardDOM = document.querySelector(
`.${keyboardDOMClass}`
) as KeyboardButton;
options = params[1];
/**
* If first parameter is an HTMLDivElement
* If first parameter is an KeyboardButton
* Consider it as the keyboard DOM element
*/
} else if (params[0] instanceof HTMLDivElement) {
@@ -213,43 +249,47 @@ class SimpleKeyboard {
*/
} else {
keyboardDOMClass = "simple-keyboard";
keyboardDOM = document.querySelector(`.${keyboardDOMClass}`);
keyboardDOM = document.querySelector(
`.${keyboardDOMClass}`
) as KeyboardButton;
options = params[0];
}
return {
keyboardDOMClass,
keyboardDOM,
options
options,
};
};
/**
* Getters
*/
getOptions = () => this.options;
getCaretPosition = () => this.caretPosition;
getCaretPositionEnd = () => this.caretPositionEnd;
getOptions = (): KeyboardOptions => this.options;
getCaretPosition = (): number => this.caretPosition;
getCaretPositionEnd = (): number => this.caretPositionEnd;
/**
* Setters
* Changes the internal caret position
* @param {number} position The caret's start position
* @param {number} positionEnd The caret's end position
*/
setCaretPosition(position, endPosition) {
setCaretPosition(position: number, endPosition = position): void {
this.caretPosition = position;
this.caretPositionEnd = endPosition || position;
this.caretPositionEnd = endPosition;
}
/**
* Handles clicks made to keyboard buttons
* @param {string} button The button's layout name.
*/
handleButtonClicked(button) {
handleButtonClicked(button: string): void {
const debug = this.options.debug;
/**
* Ignoring placeholder buttons
*/
if (button === "{//}") return false;
if (button === "{//}") return;
/**
* Calling onKeyPress
@@ -283,7 +323,7 @@ class SimpleKeyboard {
this.options.maxLength &&
this.utilities.handleMaxLength(this.input, updatedInput)
) {
return false;
return;
}
this.input[this.options.inputName] = this.utilities.getUpdatedInput(
@@ -332,7 +372,7 @@ class SimpleKeyboard {
* Handles button mousedown
*/
/* istanbul ignore next */
handleButtonMouseDown(button, e) {
handleButtonMouseDown(button: string, e: KeyboardHandlerEvent): void {
if (e) {
/**
* Handle event options
@@ -358,7 +398,7 @@ class SimpleKeyboard {
* @type {object} Time to wait until a key hold is detected
*/
if (!this.options.disableButtonHold) {
this.holdTimeout = setTimeout(() => {
this.holdTimeout = window.setTimeout(() => {
if (
(this.isMouseHold &&
// TODO: This needs to be configurable through options
@@ -375,7 +415,7 @@ class SimpleKeyboard {
) {
if (this.options.debug) console.log("Button held:", button);
this.handleButtonHold(button, e);
this.handleButtonHold(button);
}
clearTimeout(this.holdTimeout);
}, 500);
@@ -385,7 +425,7 @@ class SimpleKeyboard {
/**
* Handles button mouseup
*/
handleButtonMouseUp(button = null, e = null) {
handleButtonMouseUp(button?: string, e?: KeyboardHandlerEvent): void {
if (e) {
/**
* Handle event options
@@ -397,7 +437,7 @@ class SimpleKeyboard {
/**
* Remove active class
*/
this.recurseButtons(buttonElement => {
this.recurseButtons((buttonElement: Element) => {
buttonElement.classList.remove(this.activeButtonClass);
});
@@ -414,7 +454,7 @@ class SimpleKeyboard {
/**
* Handles container mousedown
*/
handleKeyboardContainerMouseDown(e) {
handleKeyboardContainerMouseDown(e: KeyboardHandlerEvent): void {
/**
* Handle event options
*/
@@ -425,13 +465,13 @@ class SimpleKeyboard {
* Handles button hold
*/
/* istanbul ignore next */
handleButtonHold(button) {
handleButtonHold(button: string): void {
if (this.holdInteractionTimeout) clearTimeout(this.holdInteractionTimeout);
/**
* @type {object} Timeout dictating the speed of key hold iterations
*/
this.holdInteractionTimeout = setTimeout(() => {
this.holdInteractionTimeout = window.setTimeout(() => {
if (this.isMouseHold) {
this.handleButtonClicked(button);
this.handleButtonHold(button);
@@ -444,8 +484,8 @@ class SimpleKeyboard {
/**
* Send a command to all simple-keyboard instances (if you have several instances).
*/
syncInstanceInputs() {
this.dispatch(instance => {
syncInstanceInputs(): void {
this.dispatch((instance: SimpleKeyboard) => {
instance.replaceInput(this.input);
instance.setCaretPosition(this.caretPosition, this.caretPositionEnd);
});
@@ -455,7 +495,7 @@ class SimpleKeyboard {
* Clear the keyboards input.
* @param {string} [inputName] optional - the internal input to select
*/
clearInput(inputName) {
clearInput(inputName: string): void {
inputName = inputName || this.options.inputName;
this.input[inputName] = "";
@@ -474,7 +514,7 @@ class SimpleKeyboard {
* Get the keyboards input (You can also get it from the onChange prop).
* @param {string} [inputName] optional - the internal input to select
*/
getInput(inputName, skipSync = false) {
getInput(inputName: string, skipSync = false): string {
inputName = inputName || this.options.inputName;
/**
@@ -497,11 +537,11 @@ class SimpleKeyboard {
/**
* Get all simple-keyboard inputs
*/
getAllInputs() {
getAllInputs(): KeyboardInput {
const output = {};
const inputNames = Object.keys(this.input);
inputNames.forEach(inputName => {
inputNames.forEach((inputName) => {
output[inputName] = this.getInput(inputName, true);
});
@@ -513,7 +553,7 @@ class SimpleKeyboard {
* @param {string} input the input value
* @param {string} inputName optional - the internal input to select
*/
setInput(input, inputName) {
setInput(input: string, inputName: string): void {
inputName = inputName || this.options.inputName;
this.input[inputName] = input;
@@ -527,7 +567,7 @@ class SimpleKeyboard {
* Replace the input object (`keyboard.input`)
* @param {object} inputObj The input object
*/
replaceInput(inputObj) {
replaceInput(inputObj: KeyboardInput): void {
this.input = inputObj;
}
@@ -535,7 +575,7 @@ class SimpleKeyboard {
* Set new option or modify existing ones after initialization.
* @param {object} options The options to set
*/
setOptions(options = {}) {
setOptions(options = {}): void {
const changedOptions = this.changedOptions(options);
this.options = Object.assign(this.options, options);
@@ -560,9 +600,9 @@ class SimpleKeyboard {
* Detecting changes to non-function options
* This allows us to ascertain whether a button re-render is needed
*/
changedOptions(newOptions) {
changedOptions(newOptions: Partial<KeyboardOptions>): string[] {
return Object.keys(newOptions).filter(
optionName =>
(optionName) =>
JSON.stringify(newOptions[optionName]) !==
JSON.stringify(this.options[optionName])
);
@@ -572,7 +612,7 @@ class SimpleKeyboard {
* Executing actions depending on changed options
* @param {object} options The options to set
*/
onSetOptions(options) {
onSetOptions(options: Partial<KeyboardOptions>): void {
if (options.inputName) {
/**
* inputName changed. This requires a caretPosition reset
@@ -588,7 +628,7 @@ class SimpleKeyboard {
* Remove all keyboard rows and reset keyboard values.
* Used internally between re-renders.
*/
clear() {
clear(): void {
this.keyboardDOM.innerHTML = "";
this.keyboardDOM.className = this.keyboardDOMClass;
this.buttonElements = {};
@@ -598,7 +638,8 @@ class SimpleKeyboard {
* Send a command to all simple-keyboard instances at once (if you have multiple instances).
* @param {function(instance: object, key: string)} callback Function to run on every instance
*/
dispatch(callback) {
// eslint-disable-next-line no-unused-vars
dispatch(callback: (instance: SimpleKeyboard, key?: string) => void): void {
if (!window["SimpleKeyboardInstances"]) {
console.warn(
`SimpleKeyboardInstances is not defined. Dispatch cannot be called.`
@@ -606,7 +647,7 @@ class SimpleKeyboard {
throw new Error("INSTANCES_VAR_ERROR");
}
return Object.keys(window["SimpleKeyboardInstances"]).forEach(key => {
return Object.keys(window["SimpleKeyboardInstances"]).forEach((key) => {
callback(window["SimpleKeyboardInstances"][key], key);
});
}
@@ -616,11 +657,11 @@ class SimpleKeyboard {
* @param {string} buttons List of buttons to select (separated by a space).
* @param {string} className Classes to give to the selected buttons (separated by space).
*/
addButtonTheme(buttons, className) {
if (!className || !buttons) return false;
addButtonTheme(buttons: string, className: string): void {
if (!className || !buttons) return;
buttons.split(" ").forEach(button => {
className.split(" ").forEach(classNameItem => {
buttons.split(" ").forEach((button) => {
className.split(" ").forEach((classNameItem) => {
if (!this.options.buttonTheme) this.options.buttonTheme = [];
let classNameFound = false;
@@ -628,7 +669,7 @@ class SimpleKeyboard {
/**
* If class is already defined, we add button to class definition
*/
this.options.buttonTheme.map(buttonTheme => {
this.options.buttonTheme.map((buttonTheme) => {
if (buttonTheme.class.split(" ").includes(classNameItem)) {
classNameFound = true;
@@ -648,7 +689,7 @@ class SimpleKeyboard {
if (!classNameFound) {
this.options.buttonTheme.push({
class: classNameItem,
buttons: buttons
buttons: buttons,
});
}
});
@@ -662,14 +703,14 @@ class SimpleKeyboard {
* @param {string} buttons List of buttons to select (separated by a space).
* @param {string} className Classes to give to the selected buttons (separated by space).
*/
removeButtonTheme(buttons, className) {
removeButtonTheme(buttons: string, className: string): void {
/**
* When called with empty parameters, remove all button themes
*/
if (!buttons && !className) {
this.options.buttonTheme = [];
this.render();
return false;
return;
}
/**
@@ -681,7 +722,7 @@ class SimpleKeyboard {
this.options.buttonTheme.length
) {
const buttonArray = buttons.split(" ");
buttonArray.forEach(button => {
buttonArray.forEach((button) => {
this.options.buttonTheme.map((buttonTheme, index) => {
/**
* If className is set, we affect the buttons only for that class
@@ -693,7 +734,7 @@ class SimpleKeyboard {
) {
const filteredButtonArray = buttonTheme.buttons
.split(" ")
.filter(item => item !== button);
.filter((item) => item !== button);
/**
* If buttons left, return them, otherwise, remove button Theme
@@ -718,7 +759,7 @@ class SimpleKeyboard {
* Get the DOM Element of a button. If there are several buttons with the same name, an array of the DOM Elements is returned.
* @param {string} button The button layout name to select
*/
getButtonElement(button) {
getButtonElement(button: string): KeyboardButton | KeyboardButton[] {
let output;
const buttonArr = this.buttonElements[button];
@@ -737,7 +778,7 @@ class SimpleKeyboard {
* This handles the "inputPattern" option
* by checking if the provided inputPattern passes
*/
inputPatternIsValid(inputVal) {
inputPatternIsValid(inputVal: string): boolean {
const inputPatternRaw = this.options.inputPattern;
let inputPattern;
@@ -773,7 +814,7 @@ class SimpleKeyboard {
/**
* Handles simple-keyboard event listeners
*/
setEventListeners() {
setEventListeners(): void {
/**
* Only first instance should set the event listeners
*/
@@ -795,7 +836,7 @@ class SimpleKeyboard {
/**
* Event Handler: KeyUp
*/
handleKeyUp(event) {
handleKeyUp(event: KeyboardHandlerEvent): void {
this.caretEventHandler(event);
if (this.options.physicalKeyboardHighlight) {
@@ -806,7 +847,7 @@ class SimpleKeyboard {
/**
* Event Handler: KeyDown
*/
handleKeyDown(event) {
handleKeyDown(event: KeyboardHandlerEvent): void {
if (this.options.physicalKeyboardHighlight) {
this.physicalKeyboard.handleHighlightKeyDown(event);
}
@@ -815,7 +856,7 @@ class SimpleKeyboard {
/**
* Event Handler: MouseUp
*/
handleMouseUp(event) {
handleMouseUp(event: KeyboardHandlerEvent): void {
this.caretEventHandler(event);
}
@@ -823,20 +864,20 @@ class SimpleKeyboard {
* Event Handler: TouchEnd
*/
/* istanbul ignore next */
handleTouchEnd(event) {
handleTouchEnd(event: KeyboardHandlerEvent): void {
this.caretEventHandler(event);
}
/**
* Called by {@link setEventListeners} when an event that warrants a cursor position update is triggered
*/
caretEventHandler(event) {
let targetTagName;
caretEventHandler(event: KeyboardHandlerEvent): void {
let targetTagName: string;
if (event.target.tagName) {
targetTagName = event.target.tagName.toLowerCase();
}
this.dispatch(instance => {
this.dispatch((instance) => {
const isKeyboard =
event.target === instance.keyboardDOM ||
(event.target && instance.keyboardDOM.contains(event.target));
@@ -882,10 +923,10 @@ class SimpleKeyboard {
/**
* Execute an operation on each button
*/
recurseButtons(fn) {
recurseButtons(fn: any): void {
if (!fn) return;
Object.keys(this.buttonElements).forEach(buttonName =>
Object.keys(this.buttonElements).forEach((buttonName) =>
this.buttonElements[buttonName].forEach(fn)
);
}
@@ -893,7 +934,7 @@ class SimpleKeyboard {
/**
* Destroy keyboard listeners and DOM elements
*/
destroy() {
destroy(): void {
if (this.options.debug)
console.log(
`Destroying simple-keyboard instance: ${this.currentInstanceName}`
@@ -914,7 +955,7 @@ class SimpleKeyboard {
/**
* Remove buttons
*/
let deleteButton = buttonElement => {
let deleteButton = (buttonElement: KeyboardButton) => {
buttonElement.onpointerdown = null;
buttonElement.onpointerup = null;
buttonElement.onpointercancel = null;
@@ -961,12 +1002,12 @@ class SimpleKeyboard {
/**
* Process buttonTheme option
*/
getButtonThemeClasses(button) {
getButtonThemeClasses(button: string): string[] {
const buttonTheme = this.options.buttonTheme;
let buttonClasses = [];
let buttonClasses: string[] = [];
if (Array.isArray(buttonTheme)) {
buttonTheme.forEach(themeObj => {
buttonTheme.forEach((themeObj) => {
if (
themeObj.class &&
typeof themeObj.class === "string" &&
@@ -994,11 +1035,11 @@ class SimpleKeyboard {
/**
* Process buttonAttributes option
*/
setDOMButtonAttributes(button, callback) {
setDOMButtonAttributes(button: string, callback: any): void {
const buttonAttributes = this.options.buttonAttributes;
if (Array.isArray(buttonAttributes)) {
buttonAttributes.forEach(attrObj => {
buttonAttributes.forEach((attrObj) => {
if (
attrObj.attribute &&
typeof attrObj.attribute === "string" &&
@@ -1039,7 +1080,7 @@ class SimpleKeyboard {
*/
/* istanbul ignore next */
disableContextualWindow() {
window.oncontextmenu = event => {
window.oncontextmenu = (event: KeyboardHandlerEvent) => {
if (event.target.classList.contains("hg-button")) {
event.preventDefault();
event.stopPropagation();
@@ -1145,7 +1186,7 @@ class SimpleKeyboard {
/**
* Register module
*/
registerModule = (name, initCallback) => {
registerModule = (name: string, initCallback: any) => {
if (!this.modules[name]) this.modules[name] = {};
initCallback(this.modules[name]);
@@ -1156,7 +1197,7 @@ class SimpleKeyboard {
*/
loadModules() {
if (Array.isArray(this.options.modules)) {
this.options.modules.forEach(KeyboardModule => {
this.options.modules.forEach((KeyboardModule) => {
const keyboardModule = new KeyboardModule();
keyboardModule.init(this);
});
@@ -1171,7 +1212,7 @@ class SimpleKeyboard {
/**
* Get module prop
*/
getModuleProp(name, prop) {
getModuleProp(name: string, prop: string) {
if (!this.modules[name]) return false;
return this.modules[name][prop];
@@ -1188,10 +1229,10 @@ class SimpleKeyboard {
* Parse Row DOM containers
*/
parseRowDOMContainers(
rowDOM,
rowIndex,
containerStartIndexes,
containerEndIndexes
rowDOM: HTMLDivElement,
rowIndex: number,
containerStartIndexes: number[],
containerEndIndexes: number[]
) {
const rowDOMArray = Array.from(rowDOM.children);
let removedElements = 0;
@@ -1236,7 +1277,9 @@ class SimpleKeyboard {
/**
* Inserting elements to container
*/
containedElements.forEach(element => containerDOM.appendChild(element));
containedElements.forEach((element) =>
containerDOM.appendChild(element)
);
/**
* Adding container at correct position within rowDOMArray
@@ -1251,7 +1294,7 @@ class SimpleKeyboard {
/**
* Appending rowDOM new children list
*/
rowDOMArray.forEach(element => rowDOM.appendChild(element));
rowDOMArray.forEach((element) => rowDOM.appendChild(element));
if (this.options.debug) {
console.log(
@@ -1271,9 +1314,9 @@ class SimpleKeyboard {
/**
* getKeyboardClassString
*/
getKeyboardClassString = (...baseDOMClasses) => {
getKeyboardClassString = (...baseDOMClasses: any[]) => {
const keyboardClasses = [this.keyboardDOMClass, ...baseDOMClasses].filter(
DOMClass => !!DOMClass
(DOMClass) => !!DOMClass
);
return keyboardClasses.join(" ");
@@ -1332,8 +1375,8 @@ class SimpleKeyboard {
/**
* Tracking container indicators in rows
*/
const containerStartIndexes = [];
const containerEndIndexes = [];
const containerStartIndexes: number[] = [];
const containerEndIndexes: number[] = [];
/**
* Iterating through each button in row
@@ -1400,9 +1443,12 @@ class SimpleKeyboard {
/**
* Adding buttonAttributes
*/
this.setDOMButtonAttributes(button, (attribute, value) => {
buttonDOM.setAttribute(attribute, value);
});
this.setDOMButtonAttributes(
button,
(attribute: string, value: string) => {
buttonDOM.setAttribute(attribute, value);
}
);
this.activeButtonClass = "hg-activeButton";
@@ -1418,14 +1464,14 @@ class SimpleKeyboard {
/**
* Handle PointerEvents
*/
buttonDOM.onpointerdown = e => {
buttonDOM.onpointerdown = (e: KeyboardHandlerEvent) => {
this.handleButtonClicked(button);
this.handleButtonMouseDown(button, e);
};
buttonDOM.onpointerup = e => {
buttonDOM.onpointerup = (e: KeyboardHandlerEvent) => {
this.handleButtonMouseUp(button, e);
};
buttonDOM.onpointercancel = e => {
buttonDOM.onpointercancel = (e: KeyboardHandlerEvent) => {
this.handleButtonMouseUp(button, e);
};
} else {
@@ -1436,14 +1482,14 @@ class SimpleKeyboard {
/**
* Handle touch events
*/
buttonDOM.ontouchstart = e => {
buttonDOM.ontouchstart = (e: KeyboardHandlerEvent) => {
this.handleButtonClicked(button);
this.handleButtonMouseDown(button, e);
};
buttonDOM.ontouchend = e => {
buttonDOM.ontouchend = (e: KeyboardHandlerEvent) => {
this.handleButtonMouseUp(button, e);
};
buttonDOM.ontouchcancel = e => {
buttonDOM.ontouchcancel = (e: KeyboardHandlerEvent) => {
this.handleButtonMouseUp(button, e);
};
} else {
@@ -1454,10 +1500,10 @@ class SimpleKeyboard {
this.isMouseHold = false;
this.handleButtonClicked(button);
};
buttonDOM.onmousedown = e => {
buttonDOM.onmousedown = (e: KeyboardHandlerEvent) => {
this.handleButtonMouseDown(button, e);
};
buttonDOM.onmouseup = e => {
buttonDOM.onmouseup = (e: KeyboardHandlerEvent) => {
this.handleButtonMouseUp(button, e);
};
}
@@ -1532,7 +1578,7 @@ class SimpleKeyboard {
!useMouseEvents
) {
document.onpointerup = () => this.handleButtonMouseUp();
this.keyboardDOM.onpointerdown = e =>
this.keyboardDOM.onpointerdown = (e: KeyboardHandlerEvent) =>
this.handleKeyboardContainerMouseDown(e);
} else if (useTouchEvents) {
/**
@@ -1541,14 +1587,14 @@ class SimpleKeyboard {
document.ontouchend = () => this.handleButtonMouseUp();
document.ontouchcancel = () => this.handleButtonMouseUp();
this.keyboardDOM.ontouchstart = e =>
this.keyboardDOM.ontouchstart = (e: KeyboardHandlerEvent) =>
this.handleKeyboardContainerMouseDown(e);
} else if (!useTouchEvents) {
/**
* Handling mouseup
*/
document.onmouseup = () => this.handleButtonMouseUp();
this.keyboardDOM.onmousedown = e =>
this.keyboardDOM.onmousedown = (e: KeyboardHandlerEvent) =>
this.handleKeyboardContainerMouseDown(e);
}
+186
View File
@@ -0,0 +1,186 @@
export interface KeyboardLayoutObject {
[key: string]: string[];
}
export interface KeyboardButtonTheme {
class: string;
buttons: string;
}
export interface KeyboardButtonAttributes {
attribute: string;
value: string;
buttons: string;
}
export interface KeyboardInput {
[key: string]: string
}
export type KeyboardButton = HTMLDivElement | HTMLButtonElement;
export type KeyboardHandlerEvent = PointerEvent & TouchEvent & KeyboardEvent & { target: HTMLDivElement | HTMLInputElement };
export interface KeyboardButtonElements {
[key: string]: KeyboardButton[]
}
export interface UtilitiesParams {
getOptions: () => KeyboardOptions;
getCaretPosition: () => number;
getCaretPositionEnd: () => number;
dispatch: any;
}
export interface KeyboardOptions {
/**
* Modify the keyboard layout.
*/
layout?: KeyboardLayoutObject;
/**
* Specifies which layout should be used.
*/
layoutName?: string;
/**
* Replaces variable buttons (such as `{bksp}`) with a human-friendly name (e.g.: `backspace`).
*/
display?: { [button: string]: string };
/**
* By default, when you set the display property, you replace the default one. This setting merges them instead.
*/
mergeDisplay?: boolean;
/**
* A prop to add your own css classes to the keyboard wrapper. You can add multiple classes separated by a space.
*/
theme?: string;
/**
* A prop to add your own css classes to one or several buttons.
*/
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.
*/
debug?: boolean;
/**
* Specifies whether clicking the "ENTER" button will input a newline (`\n`) or not.
*/
newLineOnEnter?: boolean;
/**
* Specifies whether clicking the "TAB" button will input a tab character (`\t`) or not.
*/
tabCharOnTab?: boolean;
/**
* Allows you to use a single simple-keyboard instance for several inputs.
*/
inputName?: string;
/**
* `number`: Restrains all of simple-keyboard inputs to a certain length. This should be used in addition to the input elements maxlengthattribute.
*
* `{ [inputName: string]: number }`: Restrains simple-keyboards individual inputs to a certain length. This should be used in addition to the input elements maxlengthattribute.
*/
maxLength?: any;
/**
* When set to true, this option synchronizes the internal input of every simple-keyboard instance.
*/
syncInstanceInputs?: boolean;
/**
* Enable highlighting of keys pressed on physical keyboard.
*/
physicalKeyboardHighlight?: boolean;
/**
* Presses keys highlighted by physicalKeyboardHighlight
*/
physicalKeyboardHighlightPress?: boolean;
/**
* Define the text color that the physical keyboard highlighted key should have.
*/
physicalKeyboardHighlightTextColor?: string;
/**
* Define the background color that the physical keyboard highlighted key should have.
*/
physicalKeyboardHighlightBgColor?: string;
/**
* Calling preventDefault for the mousedown events keeps the focus on the input.
*/
preventMouseDownDefault?: boolean;
/**
* Calling preventDefault for the mouseup events.
*/
preventMouseUpDefault?: boolean;
/**
* Stops pointer down events on simple-keyboard buttons from bubbling to parent elements.
*/
stopMouseDownPropagation?: boolean;
/**
* Stops pointer up events on simple-keyboard buttons from bubbling to parent elements.
*/
stopMouseUpPropagation?: boolean;
/**
* Render buttons as a button element instead of a div element.
*/
useButtonTag?: boolean;
/**
* A prop to ensure characters are always be added/removed at the end of the string.
*/
disableCaretPositioning?: boolean;
/**
* Restrains input(s) change to the defined regular expression pattern.
*/
inputPattern?: any;
/**
* Instructs simple-keyboard to use touch events instead of click events.
*/
useTouchEvents?: boolean;
/**
* Enable useTouchEvents automatically when touch device is detected.
*/
autoUseTouchEvents?: boolean;
/**
* Opt out of PointerEvents handling, falling back to the prior mouse event logic.
*/
useMouseEvents?: boolean;
/**
* Disable button hold action.
*/
disableButtonHold?: boolean;
/**
* Adds unicode right-to-left control characters to input return values.
*/
rtl?: boolean;
/**
* Module options can have any format
*/
[name: string]: any;
}
@@ -5,14 +5,14 @@ export const getDefaultLayout = () => {
"{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}"
".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}"
]
".com @ {space}",
],
};
};
@@ -1,13 +1,17 @@
import { KeyboardOptions, UtilitiesParams } from "../interfaces";
import Utilities from "../services/Utilities";
/**
* Physical Keyboard Service
*/
class PhysicalKeyboard {
getOptions: () => KeyboardOptions;
dispatch: any;
/**
* Creates an instance of the PhysicalKeyboard service
*/
constructor({ dispatch, getOptions }) {
constructor({ dispatch, getOptions }: Partial<UtilitiesParams>) {
/**
* @type {object} A simple-keyboard instance
*/
@@ -20,11 +24,11 @@ class PhysicalKeyboard {
Utilities.bindMethods(PhysicalKeyboard, this);
}
handleHighlightKeyDown(event) {
handleHighlightKeyDown(event: KeyboardEvent) {
const options = this.getOptions();
const buttonPressed = this.getSimpleKeyboardLayoutKey(event);
this.dispatch(instance => {
this.dispatch((instance: any) => {
const buttonDOM =
instance.getButtonElement(buttonPressed) ||
instance.getButtonElement(`{${buttonPressed}}`);
@@ -50,11 +54,11 @@ class PhysicalKeyboard {
});
}
handleHighlightKeyUp(event) {
handleHighlightKeyUp(event: KeyboardEvent) {
const options = this.getOptions();
const buttonPressed = this.getSimpleKeyboardLayoutKey(event);
this.dispatch(instance => {
this.dispatch((instance: any) => {
const buttonDOM =
instance.getButtonElement(buttonPressed) ||
instance.getButtonElement(`{${buttonPressed}}`);
@@ -81,7 +85,7 @@ class PhysicalKeyboard {
* Transforms a KeyboardEvent's "key.code" string into a simple-keyboard layout format
* @param {object} event The KeyboardEvent
*/
getSimpleKeyboardLayoutKey(event) {
getSimpleKeyboardLayoutKey(event: KeyboardEvent) {
let output;
if (
@@ -1,11 +1,25 @@
import { KeyboardInput } from "./../interfaces";
import { KeyboardOptions, UtilitiesParams } from "../interfaces";
/**
* Utility Service
*/
class Utilities {
getOptions: () => KeyboardOptions;
getCaretPosition: () => number;
getCaretPositionEnd: () => number;
dispatch: any;
maxLengthReached: boolean;
/**
* Creates an instance of the Utility service
*/
constructor({ getOptions, getCaretPosition, getCaretPositionEnd, dispatch }) {
constructor({
getOptions,
getCaretPosition,
getCaretPositionEnd,
dispatch,
}: UtilitiesParams) {
this.getOptions = getOptions;
this.getCaretPosition = getCaretPosition;
this.getCaretPositionEnd = getCaretPositionEnd;
@@ -23,7 +37,7 @@ class Utilities {
* @param {string} button The button's layout name
* @return {string} The classes to be added to the button
*/
getButtonClass(button) {
getButtonClass(button: string) {
const buttonTypeClass =
button.includes("{") && button.includes("}") && button !== "{//}"
? "functionBtn"
@@ -100,7 +114,7 @@ class Utilities {
"{numpad6}": "6",
"{numpad7}": "7",
"{numpad8}": "8",
"{numpad9}": "9"
"{numpad9}": "9",
};
}
/**
@@ -110,7 +124,11 @@ class Utilities {
* @param {object} display The provided display option
* @param {boolean} mergeDisplay Whether the provided param value should be merged with the default one.
*/
getButtonDisplayName(button, display, mergeDisplay) {
getButtonDisplayName(
button: string,
display: KeyboardOptions["display"],
mergeDisplay: boolean
) {
if (mergeDisplay) {
display = Object.assign({}, this.getDefaultDiplay(), display);
} else {
@@ -130,14 +148,18 @@ class Utilities {
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/
getUpdatedInput(
button,
input,
caretPos,
button: string,
input: string,
caretPos: number,
caretPosEnd = caretPos,
moveCaret = false
) {
const options = this.getOptions();
const commonParams = [caretPos, caretPosEnd, moveCaret];
const commonParams: [number, number, boolean] = [
caretPos,
caretPosEnd,
moveCaret,
];
let output = input;
@@ -194,10 +216,10 @@ class Utilities {
* @param {number} length Represents by how many characters the input should be moved
* @param {boolean} minus Whether the cursor should be moved to the left or not.
*/
updateCaretPos(length, minus) {
updateCaretPos(length: number, minus = false) {
const newCaretPos = this.updateCaretPosAction(length, minus);
this.dispatch(instance => {
this.dispatch((instance: any) => {
instance.setCaretPosition(newCaretPos);
});
}
@@ -208,7 +230,7 @@ class Utilities {
* @param {number} length Represents by how many characters the input should be moved
* @param {boolean} minus Whether the cursor should be moved to the left or not.
*/
updateCaretPosAction(length, minus) {
updateCaretPosAction(length: number, minus = false) {
const options = this.getOptions();
let caretPosition = this.getCaretPosition();
@@ -219,7 +241,7 @@ class Utilities {
}
if (options.debug) {
console.log("Caret at:", caretPosition, `(${this.keyboardDOMClass})`);
console.log("Caret at:", caretPosition);
}
return caretPosition;
@@ -234,8 +256,8 @@ class Utilities {
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/
addStringAt(
source,
str,
source: string,
str: string,
position = source.length,
positionEnd = source.length,
moveCaret = false
@@ -268,7 +290,7 @@ class Utilities {
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/
removeAt(
source,
source: string,
position = source.length,
positionEnd = source.length,
moveCaret = false
@@ -314,7 +336,7 @@ class Utilities {
} else {
output = source.slice(0, position) + source.slice(positionEnd);
if (moveCaret) {
this.dispatch(instance => {
this.dispatch((instance: any) => {
instance.setCaretPosition(position);
});
}
@@ -328,7 +350,7 @@ class Utilities {
* @param {object} inputObj
* @param {string} updatedInput
*/
handleMaxLength(inputObj, updatedInput) {
handleMaxLength(inputObj: KeyboardInput, updatedInput: string) {
const options = this.getOptions();
const maxLength = options.maxLength;
const currentInput = inputObj[options.inputName];
@@ -403,7 +425,7 @@ class Utilities {
* Bind all methods in a given class
*/
static bindMethods(myClass, instance) {
static bindMethods(myClass: any, instance: any) {
// eslint-disable-next-line no-unused-vars
for (const myMethod of Object.getOwnPropertyNames(myClass.prototype)) {
const excludeMethod =
@@ -419,8 +441,8 @@ class Utilities {
*
* @param {string} str The string to transform.
*/
camelCase(str) {
if (!str) return false;
camelCase(str: string): string {
if (!str) return;
return str
.toLowerCase()