Update types, tests

This commit is contained in:
Francisco Hodge 2021-03-16 00:40:23 -04:00
parent 8ba81892fe
commit cae07749f4
33 changed files with 1392 additions and 957 deletions

View File

@ -1,7 +1,6 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json",
"ecmaVersion": 6,
"sourceType": "module",
"ecmaFeatures": {

View File

@ -8,4 +8,4 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/.hg-theme-default{width:100%;user-select:none;box-sizing:border-box;overflow:hidden;touch-action:manipulation}.hg-theme-default .hg-button span{pointer-events:none}.hg-theme-default button.hg-button{border-width:0;outline:0;font-size:inherit}.hg-theme-default{font-family:HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif;background-color:#ececec;padding:5px;border-radius:5px}.hg-theme-default .hg-button{display:inline-block;flex-grow:1}.hg-theme-default .hg-row{display:flex}.hg-theme-default .hg-row:not(:last-child){margin-bottom:5px}.hg-theme-default .hg-row .hg-button-container,.hg-theme-default .hg-row .hg-button:not(:last-child){margin-right:5px}.hg-theme-default .hg-row>div:last-child{margin-right:0}.hg-theme-default .hg-row .hg-button-container{display:flex}.hg-theme-default .hg-button{box-shadow:0 0 3px -1px rgba(0,0,0,.3);height:40px;border-radius:5px;box-sizing:border-box;padding:5px;background:#fff;border-bottom:1px solid #b5b5b5;cursor:pointer;display:flex;align-items:center;justify-content:center;-webkit-tap-highlight-color:rgba(0,0,0,0)}.hg-theme-default .hg-button.hg-activeButton{background:#efefef}.hg-theme-default.hg-layout-numeric .hg-button{width:33.3%;height:60px;align-items:center;display:flex;justify-content:center}.hg-theme-default .hg-button.hg-button-numpadadd,.hg-theme-default .hg-button.hg-button-numpadenter{height:85px}.hg-theme-default .hg-button.hg-button-numpad0{width:105px}.hg-theme-default .hg-button.hg-button-com{max-width:85px}.hg-theme-default .hg-button.hg-standardBtn.hg-button-at{max-width:45px}.hg-theme-default .hg-button.hg-selectedButton{background:rgba(5,25,70,.53);color:#fff}.hg-theme-default .hg-button.hg-standardBtn[data-skbtn=".com"]{max-width:82px}.hg-theme-default .hg-button.hg-standardBtn[data-skbtn="@"]{max-width:60px}
*/.hg-theme-default{width:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;box-sizing:border-box;overflow:hidden;touch-action:manipulation;font-family:HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif;background-color:#ececec;padding:5px;border-radius:5px}.hg-theme-default .hg-button span{pointer-events:none}.hg-theme-default button.hg-button{border-width:0;outline:0;font-size:inherit}.hg-theme-default .hg-button{display:inline-block;flex-grow:1}.hg-theme-default .hg-row{display:flex}.hg-theme-default .hg-row:not(:last-child){margin-bottom:5px}.hg-theme-default .hg-row .hg-button-container,.hg-theme-default .hg-row .hg-button:not(:last-child){margin-right:5px}.hg-theme-default .hg-row>div:last-child{margin-right:0}.hg-theme-default .hg-row .hg-button-container{display:flex}.hg-theme-default .hg-button{box-shadow:0 0 3px -1px rgba(0,0,0,.3);height:40px;border-radius:5px;box-sizing:border-box;padding:5px;background:#fff;border-bottom:1px solid #b5b5b5;cursor:pointer;display:flex;align-items:center;justify-content:center;-webkit-tap-highlight-color:rgba(0,0,0,0)}.hg-theme-default .hg-button.hg-standardBtn{width:20px}.hg-theme-default .hg-button.hg-functionBtn .hg-theme-default .hg-button.hg-activeButton{background:#efefef}.hg-theme-default.hg-layout-numeric .hg-button{width:33.3%;height:60px;align-items:center;display:flex;justify-content:center}.hg-theme-default .hg-button.hg-button-numpadadd,.hg-theme-default .hg-button.hg-button-numpadenter{height:85px}.hg-theme-default .hg-button.hg-button-numpad0{width:105px}.hg-theme-default .hg-button.hg-button-com{max-width:85px}.hg-theme-default .hg-button.hg-standardBtn.hg-button-at{max-width:45px}.hg-theme-default .hg-button.hg-selectedButton{background:rgba(5,25,70,.53);color:#fff}.hg-theme-default .hg-button.hg-standardBtn[data-skbtn=".com"]{max-width:82px}.hg-theme-default .hg-button.hg-standardBtn[data-skbtn="@"]{max-width:60px}.hg-candidate-box{display:inline-flex;border-radius:5px;position:absolute;background:#ececec;border-bottom:2px solid #b5b5b5;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;min-width:272px;transform:translateY(-100%);margin-top:-10px}ul.hg-candidate-box-list{display:flex;list-style:none;padding:0;margin:0;flex:1}li.hg-candidate-box-list-item{height:40px;width:40px;display:flex;align-items:center;justify-content:center}li.hg-candidate-box-list-item:hover{background:rgba(0,0,0,.03);cursor:pointer}li.hg-candidate-box-list-item:active{background:rgba(0,0,0,.1)}.hg-candidate-box-prev:before{content:"◄"}.hg-candidate-box-next:before{content:"►"}.hg-candidate-box-next,.hg-candidate-box-prev{display:flex;align-items:center;padding:0 10px;background:#d0d0d0;color:#969696;cursor:pointer}.hg-candidate-box-next{border-top-right-radius:5px;border-bottom-right-radius:5px}.hg-candidate-box-prev{border-top-left-radius:5px;border-bottom-left-radius:5px}.hg-candidate-box-btn-active{color:#444}

File diff suppressed because one or more lines are too long

618
build/index.d.ts vendored
View File

@ -1,618 +0,0 @@
declare module "services/KeyboardLayout" {
export const getDefaultLayout: () => {
default: string[];
shift: string[];
};
}
declare module "interfaces" {
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;
}
}
declare module "services/Utilities" {
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, }: UtilitiesParams);
/**
* Adds default classes to a given button
*
* @param {string} button The button's layout name
* @return {string} The classes to be added to the button
*/
getButtonClass(button: string): string;
/**
* Default button display labels
*/
getDefaultDiplay(): {
"{bksp}": string;
"{backspace}": string;
"{enter}": string;
"{shift}": string;
"{shiftleft}": string;
"{shiftright}": string;
"{alt}": string;
"{s}": string;
"{tab}": string;
"{lock}": string;
"{capslock}": string;
"{accept}": string;
"{space}": string;
"{//}": string;
"{esc}": string;
"{escape}": string;
"{f1}": string;
"{f2}": string;
"{f3}": string;
"{f4}": string;
"{f5}": string;
"{f6}": string;
"{f7}": string;
"{f8}": string;
"{f9}": string;
"{f10}": string;
"{f11}": string;
"{f12}": string;
"{numpaddivide}": string;
"{numlock}": string;
"{arrowup}": string;
"{arrowleft}": string;
"{arrowdown}": string;
"{arrowright}": string;
"{prtscr}": string;
"{scrolllock}": string;
"{pause}": string;
"{insert}": string;
"{home}": string;
"{pageup}": string;
"{delete}": string;
"{end}": string;
"{pagedown}": string;
"{numpadmultiply}": string;
"{numpadsubtract}": string;
"{numpadadd}": string;
"{numpadenter}": string;
"{period}": string;
"{numpaddecimal}": string;
"{numpad0}": string;
"{numpad1}": string;
"{numpad2}": string;
"{numpad3}": string;
"{numpad4}": string;
"{numpad5}": string;
"{numpad6}": string;
"{numpad7}": string;
"{numpad8}": string;
"{numpad9}": string;
};
/**
* Returns the display (label) name for a given button
*
* @param {string} button The button's layout name
* @param {object} display The provided display option
* @param {boolean} mergeDisplay Whether the provided param value should be merged with the default one.
*/
getButtonDisplayName(button: string, display: KeyboardOptions["display"], mergeDisplay: boolean): string;
/**
* Returns the updated input resulting from clicking a given button
*
* @param {string} button The button's layout name
* @param {string} input The input string
* @param {number} caretPos The cursor's current position
* @param {number} caretPosEnd The cursor's current end position
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/
getUpdatedInput(button: string, input: string, caretPos: number, caretPosEnd?: number, moveCaret?: boolean): string;
/**
* Moves the cursor position by a given amount
*
* @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: number, minus?: boolean): void;
/**
* Action method of updateCaretPos
*
* @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: number, minus?: boolean): number;
/**
* Adds a string to the input at a given position
*
* @param {string} source The source input
* @param {string} str The string to add
* @param {number} position The (cursor) position where the string should be added
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/
addStringAt(source: string, str: string, position?: number, positionEnd?: number, moveCaret?: boolean): string;
/**
* Removes an amount of characters at a given position
*
* @param {string} source The source input
* @param {number} position The (cursor) position from where the characters should be removed
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/
removeAt(source: string, position?: number, positionEnd?: number, moveCaret?: boolean): string;
/**
* Determines whether the maxLength has been reached. This function is called when the maxLength option it set.
*
* @param {object} inputObj
* @param {string} updatedInput
*/
handleMaxLength(inputObj: KeyboardInput, updatedInput: string): boolean;
/**
* Gets the current value of maxLengthReached
*/
isMaxLengthReached(): boolean;
/**
* Determines whether a touch device is being used
*/
isTouchDevice(): number | true;
/**
* Determines whether pointer events are supported
*/
pointerEventsSupported(): {
new (type: string, eventInitDict?: PointerEventInit): PointerEvent;
prototype: PointerEvent;
};
/**
* Bind all methods in a given class
*/
static bindMethods(myClass: any, instance: any): void;
/**
* Transforms an arbitrary string to camelCase
*
* @param {string} str The string to transform.
*/
camelCase(str: string): string;
static noop: () => void;
}
export default Utilities;
}
declare module "services/PhysicalKeyboard" {
import { KeyboardOptions, UtilitiesParams } from "interfaces";
/**
* Physical Keyboard Service
*/
class PhysicalKeyboard {
getOptions: () => KeyboardOptions;
dispatch: any;
/**
* Creates an instance of the PhysicalKeyboard service
*/
constructor({ dispatch, getOptions }: Partial<UtilitiesParams>);
handleHighlightKeyDown(event: KeyboardEvent): void;
handleHighlightKeyUp(event: KeyboardEvent): void;
/**
* Transforms a KeyboardEvent's "key.code" string into a simple-keyboard layout format
* @param {object} event The KeyboardEvent
*/
getSimpleKeyboardLayoutKey(event: KeyboardEvent): string;
}
export default PhysicalKeyboard;
}
declare module "components/Keyboard" {
import "./Keyboard.css";
import PhysicalKeyboard from "services/PhysicalKeyboard";
import { KeyboardOptions, KeyboardInput, KeyboardButtonElements, KeyboardHandlerEvent, KeyboardButton } from "interfaces";
/**
* Root class for simple-keyboard
* This class:
* - Parses the options
* - Renders the rows and buttons
* - 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: []);
/**
* parseParams
*/
handleParams: (params: any[]) => {
keyboardDOMClass: string;
keyboardDOM: KeyboardButton;
options: Partial<KeyboardOptions>;
};
/**
* Getters
*/
getOptions: () => KeyboardOptions;
getCaretPosition: () => number;
getCaretPositionEnd: () => 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, endPosition?: number): void;
/**
* Handles clicks made to keyboard buttons
* @param {string} button The button's layout name.
*/
handleButtonClicked(button: string): void;
/**
* Handles button mousedown
*/
handleButtonMouseDown(button: string, e: KeyboardHandlerEvent): void;
/**
* Handles button mouseup
*/
handleButtonMouseUp(button?: string, e?: KeyboardHandlerEvent): void;
/**
* Handles container mousedown
*/
handleKeyboardContainerMouseDown(e: KeyboardHandlerEvent): void;
/**
* Handles button hold
*/
handleButtonHold(button: string): void;
/**
* Send a command to all simple-keyboard instances (if you have several instances).
*/
syncInstanceInputs(): void;
/**
* Clear the keyboards 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, skipSync?: boolean): string;
/**
* Get all simple-keyboard inputs
*/
getAllInputs(): KeyboardInput;
/**
* 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;
/**
* Replace the input object (`keyboard.input`)
* @param {object} inputObj The input object
*/
replaceInput(inputObj: KeyboardInput): void;
/**
* Set new option or modify existing ones after initialization.
* @param {object} options The options to set
*/
setOptions(options?: {}): void;
/**
* Detecting changes to non-function options
* This allows us to ascertain whether a button re-render is needed
*/
changedOptions(newOptions: Partial<KeyboardOptions>): string[];
/**
* Executing actions depending on changed options
* @param {object} options The options to set
*/
onSetOptions(options: Partial<KeyboardOptions>): void;
/**
* Remove all keyboard rows and reset keyboard values.
* Used internally between re-renders.
*/
clear(): 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: SimpleKeyboard, key?: string) => void): void;
/**
* 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;
/**
* 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): KeyboardButton | KeyboardButton[];
/**
* This handles the "inputPattern" option
* by checking if the provided inputPattern passes
*/
inputPatternIsValid(inputVal: string): boolean;
/**
* Handles simple-keyboard event listeners
*/
setEventListeners(): void;
/**
* Event Handler: KeyUp
*/
handleKeyUp(event: KeyboardHandlerEvent): void;
/**
* Event Handler: KeyDown
*/
handleKeyDown(event: KeyboardHandlerEvent): void;
/**
* Event Handler: MouseUp
*/
handleMouseUp(event: KeyboardHandlerEvent): void;
/**
* Event Handler: TouchEnd
*/
handleTouchEnd(event: KeyboardHandlerEvent): void;
/**
* Called by {@link setEventListeners} when an event that warrants a cursor position update is triggered
*/
caretEventHandler(event: KeyboardHandlerEvent): void;
/**
* Execute an operation on each button
*/
recurseButtons(fn: any): void;
/**
* Destroy keyboard listeners and DOM elements
*/
destroy(): void;
/**
* Process buttonTheme option
*/
getButtonThemeClasses(button: string): string[];
/**
* Process buttonAttributes option
*/
setDOMButtonAttributes(button: string, callback: any): void;
onTouchDeviceDetected(): void;
/**
* Disabling contextual window for hg-button
*/
disableContextualWindow(): void;
/**
* Process autoTouchEvents option
*/
processAutoTouchEvents(): void;
/**
* Executes the callback function once simple-keyboard is rendered for the first time (on initialization).
*/
onInit(): void;
/**
* Executes the callback function before a 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 all modules have been loaded
*/
onModulesLoaded(): void;
/**
* Register module
*/
registerModule: (name: string, initCallback: any) => void;
/**
* Load modules
*/
loadModules(): void;
/**
* Get module prop
*/
getModuleProp(name: string, prop: string): any;
/**
* getModulesList
*/
getModulesList(): string[];
/**
* Parse Row DOM containers
*/
parseRowDOMContainers(rowDOM: HTMLDivElement, rowIndex: number, containerStartIndexes: number[], containerEndIndexes: number[]): HTMLDivElement;
/**
* getKeyboardClassString
*/
getKeyboardClassString: (...baseDOMClasses: any[]) => string;
/**
* Renders rows and buttons as per options
*/
render(): void;
}
export default SimpleKeyboard;
}
declare module "index" {
import SimpleKeyboard from "components/Keyboard";
export default SimpleKeyboard;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,14 @@
import "./css/CandidateBox.css";
import Utilities from "../services/Utilities";
import { CandidateBoxParams, CandidateBoxRenderParams, CandidateBoxShowParams } from "./../interfaces";
declare class CandidateBox {
utilities: Utilities;
candidateBoxElement: HTMLDivElement;
pageIndex: number;
pageSize: number;
constructor({ utilities }: CandidateBoxParams);
destroy(): void;
show({ candidateValue, targetElement, onSelect, }: CandidateBoxShowParams): void;
renderPage({ candidateListPages, targetElement, pageIndex, nbPages, onItemSelected, }: CandidateBoxRenderParams): void;
}
export default CandidateBox;

277
build/types/components/Keyboard.d.ts vendored Normal file
View File

@ -0,0 +1,277 @@
import "./css/Keyboard.css";
import PhysicalKeyboard from "../services/PhysicalKeyboard";
import { KeyboardOptions, KeyboardInput, KeyboardButtonElements, KeyboardHandlerEvent, KeyboardElement, KeyboardParams } from "../interfaces";
import CandidateBox from "./CandidateBox";
/**
* Root class for simple-keyboard.
* This class:
* - Parses the options
* - Renders the rows and buttons
* - Handles button functionality
*/
declare class SimpleKeyboard {
input: KeyboardInput;
options: KeyboardOptions;
utilities: any;
caretPosition: number;
caretPositionEnd: number;
keyboardDOM: KeyboardElement;
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;
candidateBox: CandidateBox;
keyboardRowsDOM: KeyboardElement;
/**
* 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: KeyboardParams);
/**
* parseParams
*/
handleParams: (params: KeyboardParams) => {
keyboardDOMClass: string;
keyboardDOM: KeyboardElement;
options: Partial<KeyboardOptions>;
};
/**
* Getters
*/
getOptions: () => KeyboardOptions;
getCaretPosition: () => number;
getCaretPositionEnd: () => 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, endPosition?: number): void;
/**
* Retrieve the candidates for a given input
* @param input The input string to check
*/
getInputCandidates(input: string): {
candidateKey: string;
candidateValue: string;
} | Record<string, never>;
/**
* Shows a suggestion box with a list of candidate words
* @param candidates The chosen candidates string as defined in the layoutCandidates option
* @param targetElement The element next to which the candidates box will be shown
*/
showCandidatesBox(candidateKey: string, candidateValue: string, targetElement: KeyboardElement): void;
/**
* Handles clicks made to keyboard buttons
* @param {string} button The button's layout name.
*/
handleButtonClicked(button: string, e?: KeyboardHandlerEvent): void;
/**
* Handles button mousedown
*/
handleButtonMouseDown(button: string, e: KeyboardHandlerEvent): void;
/**
* Handles button mouseup
*/
handleButtonMouseUp(button?: string, e?: KeyboardHandlerEvent): void;
/**
* Handles container mousedown
*/
handleKeyboardContainerMouseDown(e: KeyboardHandlerEvent): void;
/**
* Handles button hold
*/
handleButtonHold(button: string): void;
/**
* Send a command to all simple-keyboard instances (if you have several instances).
*/
syncInstanceInputs(): void;
/**
* Clear the keyboards 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, skipSync?: boolean): string;
/**
* Get all simple-keyboard inputs
*/
getAllInputs(): KeyboardInput;
/**
* Set the keyboards input.
* @param {string} input the input value
* @param {string} inputName optional - the internal input to select
*/
setInput(input: string, inputName?: string, skipSync?: boolean): void;
/**
* Replace the input object (`keyboard.input`)
* @param {object} inputObj The input object
*/
replaceInput(inputObj: KeyboardInput): void;
/**
* Set new option or modify existing ones after initialization.
* @param {object} options The options to set
*/
setOptions(options?: {}): void;
/**
* Detecting changes to non-function options
* This allows us to ascertain whether a button re-render is needed
*/
changedOptions(newOptions: Partial<KeyboardOptions>): string[];
/**
* Executing actions depending on changed options
* @param {object} options The options to set
*/
onSetOptions(changedOptions?: string[]): void;
/**
* Remove all keyboard rows and reset keyboard values.
* Used internally between re-renders.
*/
resetRows(): 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: SimpleKeyboard, key?: string) => void): void;
/**
* 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;
/**
* 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): KeyboardElement | KeyboardElement[];
/**
* This handles the "inputPattern" option
* by checking if the provided inputPattern passes
*/
inputPatternIsValid(inputVal: string): boolean;
/**
* Handles simple-keyboard event listeners
*/
setEventListeners(): void;
/**
* Event Handler: KeyUp
*/
handleKeyUp(event: KeyboardHandlerEvent): void;
/**
* Event Handler: KeyDown
*/
handleKeyDown(event: KeyboardHandlerEvent): void;
/**
* Event Handler: MouseUp
*/
handleMouseUp(event: KeyboardHandlerEvent): void;
/**
* Event Handler: TouchEnd
*/
handleTouchEnd(event: KeyboardHandlerEvent): void;
/**
* Event Handler: Select
*/
handleSelect(event: KeyboardHandlerEvent): void;
/**
* Called by {@link setEventListeners} when an event that warrants a cursor position update is triggered
*/
caretEventHandler(event: KeyboardHandlerEvent): void;
/**
* Execute an operation on each button
*/
recurseButtons(fn: any): void;
/**
* Destroy keyboard listeners and DOM elements
*/
destroy(): void;
/**
* Process buttonTheme option
*/
getButtonThemeClasses(button: string): string[];
/**
* Process buttonAttributes option
*/
setDOMButtonAttributes(button: string, callback: any): void;
onTouchDeviceDetected(): void;
/**
* Disabling contextual window for hg-button
*/
disableContextualWindow(): void;
/**
* Process autoTouchEvents option
*/
processAutoTouchEvents(): void;
/**
* Executes the callback function once simple-keyboard is rendered for the first time (on initialization).
*/
onInit(): void;
/**
* Executes the callback function before a 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 all modules have been loaded
*/
onModulesLoaded(): void;
/**
* Register module
*/
registerModule: (name: string, initCallback: any) => void;
/**
* Load modules
*/
loadModules(): void;
/**
* Get module prop
*/
getModuleProp(name: string, prop: string): any;
/**
* getModulesList
*/
getModulesList(): string[];
/**
* Parse Row DOM containers
*/
parseRowDOMContainers(rowDOM: HTMLDivElement, rowIndex: number, containerStartIndexes: number[], containerEndIndexes: number[]): HTMLDivElement;
/**
* getKeyboardClassString
*/
getKeyboardClassString: (...baseDOMClasses: any[]) => string;
/**
* Renders rows and buttons as per options
*/
render(): void;
}
export default SimpleKeyboard;

3
build/types/index.d.ts vendored Normal file
View File

@ -0,0 +1,3 @@
import "./polyfills";
import SimpleKeyboard from "./components/Keyboard";
export default SimpleKeyboard;

200
build/types/interfaces.d.ts vendored Normal file
View File

@ -0,0 +1,200 @@
import SimpleKeyboard from "./components/Keyboard";
import Utilities from "./services/Utilities";
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 declare type KeyboardParams = [KeyboardOptions] | [string | HTMLDivElement, KeyboardOptions];
export declare type CandidateBoxParams = {
utilities: Utilities;
};
export declare type CandidateBoxShowParams = {
candidateValue: string;
targetElement: KeyboardElement;
onSelect: (selectedCandidate: string) => void;
};
export declare type CandidateBoxRenderParams = {
candidateListPages: string[][];
targetElement: KeyboardElement;
pageIndex: number;
nbPages: number;
onItemSelected: (selectedCandidate: string) => void;
};
export declare type KeyboardElement = HTMLDivElement | HTMLButtonElement;
export declare type KeyboardHandlerEvent = PointerEvent & TouchEvent & KeyboardEvent & {
target: HTMLDivElement | HTMLInputElement;
};
export interface KeyboardButtonElements {
[key: string]: KeyboardElement[];
}
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;
/**
* Enable input method editor candidate list support.
*/
enableLayoutCandidates?: boolean;
/**
* Character suggestions to be shown on certain key presses
*/
layoutCandidates?: {
[key: string]: string;
};
/**
* Exclude buttons from layout
*/
excludeFromLayout?: {
[key: string]: string[];
};
/**
* Determine size of layout candidate list
*/
layoutCandidatesPageSize?: number;
/**
* Executes the callback function every time simple-keyboard is rendered (e.g: when you change layouts).
*/
onRender?: (instance?: SimpleKeyboard) => void;
/**
* Executes the callback function once simple-keyboard is rendered for the first time (on initialization).
*/
onInit?: (instance?: SimpleKeyboard) => void;
/**
* Module options can have any format
*/
[name: string]: any;
}

0
build/types/polyfills.d.ts vendored Normal file
View File

View File

@ -0,0 +1,4 @@
export declare const getDefaultLayout: () => {
default: string[];
shift: string[];
};

View File

@ -0,0 +1,20 @@
import { KeyboardOptions, UtilitiesParams } from "../interfaces";
/**
* Physical Keyboard Service
*/
declare class PhysicalKeyboard {
getOptions: () => KeyboardOptions;
dispatch: any;
/**
* Creates an instance of the PhysicalKeyboard service
*/
constructor({ dispatch, getOptions }: Partial<UtilitiesParams>);
handleHighlightKeyDown(event: KeyboardEvent): void;
handleHighlightKeyUp(event: KeyboardEvent): void;
/**
* Transforms a KeyboardEvent's "key.code" string into a simple-keyboard layout format
* @param {object} event The KeyboardEvent
*/
getSimpleKeyboardLayoutKey(event: KeyboardEvent): string;
}
export default PhysicalKeyboard;

181
build/types/services/Utilities.d.ts vendored Normal file
View File

@ -0,0 +1,181 @@
import { KeyboardInput } from "./../interfaces";
import { KeyboardOptions, UtilitiesParams } from "../interfaces";
/**
* Utility Service
*/
declare class Utilities {
getOptions: () => KeyboardOptions;
getCaretPosition: () => number;
getCaretPositionEnd: () => number;
dispatch: any;
maxLengthReached: boolean;
/**
* Creates an instance of the Utility service
*/
constructor({ getOptions, getCaretPosition, getCaretPositionEnd, dispatch, }: UtilitiesParams);
/**
* Retrieve button type
*
* @param {string} button The button's layout name
* @return {string} The button type
*/
getButtonType(button: string): string;
/**
* Adds default classes to a given button
*
* @param {string} button The button's layout name
* @return {string} The classes to be added to the button
*/
getButtonClass(button: string): string;
/**
* Default button display labels
*/
getDefaultDiplay(): {
"{bksp}": string;
"{backspace}": string;
"{enter}": string;
"{shift}": string;
"{shiftleft}": string;
"{shiftright}": string;
"{alt}": string;
"{s}": string;
"{tab}": string;
"{lock}": string;
"{capslock}": string;
"{accept}": string;
"{space}": string;
"{//}": string;
"{esc}": string;
"{escape}": string;
"{f1}": string;
"{f2}": string;
"{f3}": string;
"{f4}": string;
"{f5}": string;
"{f6}": string;
"{f7}": string;
"{f8}": string;
"{f9}": string;
"{f10}": string;
"{f11}": string;
"{f12}": string;
"{numpaddivide}": string;
"{numlock}": string;
"{arrowup}": string;
"{arrowleft}": string;
"{arrowdown}": string;
"{arrowright}": string;
"{prtscr}": string;
"{scrolllock}": string;
"{pause}": string;
"{insert}": string;
"{home}": string;
"{pageup}": string;
"{delete}": string;
"{end}": string;
"{pagedown}": string;
"{numpadmultiply}": string;
"{numpadsubtract}": string;
"{numpadadd}": string;
"{numpadenter}": string;
"{period}": string;
"{numpaddecimal}": string;
"{numpad0}": string;
"{numpad1}": string;
"{numpad2}": string;
"{numpad3}": string;
"{numpad4}": string;
"{numpad5}": string;
"{numpad6}": string;
"{numpad7}": string;
"{numpad8}": string;
"{numpad9}": string;
};
/**
* Returns the display (label) name for a given button
*
* @param {string} button The button's layout name
* @param {object} display The provided display option
* @param {boolean} mergeDisplay Whether the provided param value should be merged with the default one.
*/
getButtonDisplayName(button: string, display: KeyboardOptions["display"], mergeDisplay: boolean): string;
/**
* Returns the updated input resulting from clicking a given button
*
* @param {string} button The button's layout name
* @param {string} input The input string
* @param {number} caretPos The cursor's current position
* @param {number} caretPosEnd The cursor's current end position
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/
getUpdatedInput(button: string, input: string, caretPos: number, caretPosEnd?: number, moveCaret?: boolean): string;
/**
* Moves the cursor position by a given amount
*
* @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: number, minus?: boolean): void;
/**
* Action method of updateCaretPos
*
* @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: number, minus?: boolean): number;
/**
* Adds a string to the input at a given position
*
* @param {string} source The source input
* @param {string} str The string to add
* @param {number} position The (cursor) position where the string should be added
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/
addStringAt(source: string, str: string, position?: number, positionEnd?: number, moveCaret?: boolean): string;
/**
* Removes an amount of characters at a given position
*
* @param {string} source The source input
* @param {number} position The (cursor) position from where the characters should be removed
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/
removeAt(source: string, position?: number, positionEnd?: number, moveCaret?: boolean): string;
/**
* Determines whether the maxLength has been reached. This function is called when the maxLength option it set.
*
* @param {object} inputObj
* @param {string} updatedInput
*/
handleMaxLength(inputObj: KeyboardInput, updatedInput: string): boolean;
/**
* Gets the current value of maxLengthReached
*/
isMaxLengthReached(): boolean;
/**
* Determines whether a touch device is being used
*/
isTouchDevice(): number | true;
/**
* Determines whether pointer events are supported
*/
pointerEventsSupported(): boolean;
/**
* Bind all methods in a given class
*/
static bindMethods(myClass: any, instance: any): void;
/**
* Transforms an arbitrary string to camelCase
*
* @param {string} str The string to transform.
*/
camelCase(str: string): string;
/**
* Split array into chunks
*/
chunkArray<T>(arr: T[], size: number): T[][];
/**
* Reusable empty function
*/
static noop: () => void;
}
export default Utilities;

View File

@ -1,12 +0,0 @@
<html>
<head>
<link rel="stylesheet" href="css/index.css">
<link rel="stylesheet" href="main.css">
</head>
<body>
<input class="input" placeholder="Tap on the virtual keyboard to start" />
<div class="simple-keyboard"></div>
<script src="index.js"></script>
<script src="main.js"></script>
</body>
</html>

View File

@ -1,12 +0,0 @@
input {
width: 100%;
height: 100px;
padding: 20px;
font-size: 20px;
border: none;
box-sizing: border-box;
}
.simple-keyboard {
max-width: 850px;
}

View File

@ -1,38 +0,0 @@
const Keyboard = window.SimpleKeyboard.default;
const keyboard = new Keyboard({
onChange: input => onChange(input),
onKeyPress: button => onKeyPress(button)
});
/**
* Update simple-keyboard when input is changed directly
*/
document.querySelector(".input").addEventListener("input", event => {
keyboard.setInput(event.target.value);
});
console.log(keyboard);
function onChange(input) {
document.querySelector(".input").value = input;
console.log("Input changed", input);
}
function onKeyPress(button) {
console.log("Button pressed", button);
/**
* If you want to handle the shift and caps lock buttons
*/
if (button === "{shift}" || button === "{lock}") handleShift();
}
function handleShift() {
const currentLayout = keyboard.options.layoutName;
const shiftToggle = currentLayout === "default" ? "shift" : "default";
keyboard.setOptions({
layoutName: shiftToggle
});
}

409
package-lock.json generated
View File

@ -988,6 +988,14 @@
"requires": {
"core-js": "^2.6.5",
"regenerator-runtime": "^0.13.4"
},
"dependencies": {
"core-js": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
"dev": true
}
}
},
"@babel/preset-env": {
@ -1967,6 +1975,12 @@
"@types/node": "*"
}
},
"@types/html-minifier-terser": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
"integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==",
"dev": true
},
"@types/http-proxy": {
"version": "1.17.5",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.5.tgz",
@ -2000,6 +2014,16 @@
"@types/istanbul-lib-report": "*"
}
},
"@types/jest": {
"version": "26.0.20",
"resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.20.tgz",
"integrity": "sha512-9zi2Y+5USJRxd0FsahERhBwlcvFh6D2GLQnY2FH2BzK8J9s9omvNHIbvABwIluXa0fD8XVKMLTO0aOEuUfACAA==",
"dev": true,
"requires": {
"jest-diff": "^26.0.0",
"pretty-format": "^26.0.0"
}
},
"@types/json-schema": {
"version": "7.0.7",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz",
@ -2018,6 +2042,12 @@
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
"@types/parse-json": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
},
"@types/prettier": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.2.tgz",
@ -2645,6 +2675,20 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true
},
"autoprefixer": {
"version": "10.2.5",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.2.5.tgz",
"integrity": "sha512-7H4AJZXvSsn62SqZyJCP+1AWwOuoYpUfK6ot9vm0e87XD6mT8lDywc9D9OTJPMULyGcvmIxzTAMeG2Cc+YX+fA==",
"dev": true,
"requires": {
"browserslist": "^4.16.3",
"caniuse-lite": "^1.0.30001196",
"colorette": "^1.2.2",
"fraction.js": "^4.0.13",
"normalize-range": "^0.1.2",
"postcss-value-parser": "^4.1.0"
}
},
"available-typed-arrays": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz",
@ -3408,6 +3452,24 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
},
"camel-case": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
"integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
"dev": true,
"requires": {
"pascal-case": "^3.1.2",
"tslib": "^2.0.3"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==",
"dev": true
}
}
},
"camelcase": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz",
@ -3602,6 +3664,23 @@
}
}
},
"clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
"integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==",
"dev": true,
"requires": {
"source-map": "~0.6.0"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
}
}
},
"clean-stack": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
@ -3810,9 +3889,9 @@
"dev": true
},
"core-js": {
"version": "2.6.12",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.9.1.tgz",
"integrity": "sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg==",
"dev": true
},
"core-js-compat": {
@ -4533,6 +4612,15 @@
"esutils": "^2.0.2"
}
},
"dom-converter": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
"integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==",
"dev": true,
"requires": {
"utila": "~0.4"
}
},
"dom-serializer": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
@ -4574,6 +4662,15 @@
}
}
},
"domhandler": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
"integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
"dev": true,
"requires": {
"domelementtype": "1"
}
},
"domutils": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
@ -4584,6 +4681,24 @@
"domelementtype": "1"
}
},
"dot-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
"integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
"dev": true,
"requires": {
"no-case": "^3.0.4",
"tslib": "^2.0.3"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==",
"dev": true
}
}
},
"dot-prop": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
@ -5668,6 +5783,12 @@
"integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
"dev": true
},
"fraction.js": {
"version": "4.0.13",
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.0.13.tgz",
"integrity": "sha512-E1fz2Xs9ltlUp+qbiyx9wmt2n9dRzPsS11Jtdb8D2o+cC7wr9xkkKsVKJuBX0ST+LVS+LhLO+SbLJNtfWcJvXA==",
"dev": true
},
"fragment-cache": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
@ -5944,6 +6065,12 @@
}
}
},
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true
},
"hex-color-regex": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
@ -6007,6 +6134,94 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true
},
"html-minifier-terser": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
"integrity": "sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==",
"dev": true,
"requires": {
"camel-case": "^4.1.1",
"clean-css": "^4.2.3",
"commander": "^4.1.1",
"he": "^1.2.0",
"param-case": "^3.0.3",
"relateurl": "^0.2.7",
"terser": "^4.6.3"
},
"dependencies": {
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
"integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
"dev": true,
"requires": {
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
},
"dependencies": {
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
}
}
}
}
},
"html-webpack-plugin": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.3.0.tgz",
"integrity": "sha512-BjtSQCy5J0e9EDV+NZ4i+w36N9avXfQDF4QoAV9YAECG50x6/2tfJkFb0ws9eTcszLdyg4vs6FI6hMgVzVoQqw==",
"dev": true,
"requires": {
"@types/html-minifier-terser": "^5.0.0",
"html-minifier-terser": "^5.0.1",
"lodash": "^4.17.20",
"pretty-error": "^2.1.1",
"tapable": "^2.0.0"
}
},
"htmlparser2": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
"integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
"dev": true,
"requires": {
"domelementtype": "^1.3.1",
"domhandler": "^2.3.0",
"domutils": "^1.5.1",
"entities": "^1.1.1",
"inherits": "^2.0.1",
"readable-stream": "^3.1.1"
},
"dependencies": {
"entities": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
"dev": true
},
"readable-stream": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
"dev": true,
"requires": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
"util-deprecate": "^1.0.1"
}
}
}
},
"http-deceiver": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
@ -8297,6 +8512,12 @@
"integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
"dev": true
},
"klona": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz",
"integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==",
"dev": true
},
"last-call-webpack-plugin": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz",
@ -8414,6 +8635,23 @@
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
"dev": true
},
"lower-case": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
"integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
"dev": true,
"requires": {
"tslib": "^2.0.3"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==",
"dev": true
}
}
},
"lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@ -8702,9 +8940,9 @@
"dev": true
},
"nanoid": {
"version": "3.1.20",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz",
"integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==",
"version": "3.1.22",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz",
"integrity": "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ==",
"dev": true
},
"nanomatch": {
@ -8750,6 +8988,24 @@
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
"dev": true
},
"no-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
"integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
"dev": true,
"requires": {
"lower-case": "^2.0.2",
"tslib": "^2.0.3"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==",
"dev": true
}
}
},
"node-forge": {
"version": "0.10.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
@ -8819,6 +9075,12 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true
},
"normalize-range": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
"integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
"dev": true
},
"normalize-url": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz",
@ -9130,6 +9392,24 @@
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true
},
"param-case": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
"integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
"dev": true,
"requires": {
"dot-case": "^3.0.4",
"tslib": "^2.0.3"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==",
"dev": true
}
}
},
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -9163,6 +9443,24 @@
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
"dev": true
},
"pascal-case": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
"integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
"dev": true,
"requires": {
"no-case": "^3.0.4",
"tslib": "^2.0.3"
},
"dependencies": {
"tslib": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.1.0.tgz",
"integrity": "sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==",
"dev": true
}
}
},
"pascalcase": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
@ -9283,9 +9581,9 @@
"dev": true
},
"postcss": {
"version": "8.2.7",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.7.tgz",
"integrity": "sha512-DsVLH3xJzut+VT+rYr0mtvOtpTjSyqDwPf5EZWXcb0uAKfitGpTY9Ec+afi2+TgdN8rWS9Cs88UDYehKo/RvOw==",
"version": "8.2.8",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.8.tgz",
"integrity": "sha512-1F0Xb2T21xET7oQV9eKuctbM9S7BC0fetoHCc4H13z0PT6haiRLP4T0ZY4XWh7iLP0usgqykT6p9B2RtOf4FPw==",
"dev": true,
"requires": {
"colorette": "^1.2.2",
@ -9579,6 +9877,41 @@
}
}
},
"postcss-loader": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-5.2.0.tgz",
"integrity": "sha512-uSuCkENFeUaOYsKrXm0eNNgVIxc71z8RcckLMbVw473rGojFnrUeqEz6zBgXsH2q1EIzXnO/4pEz9RhALjlITA==",
"dev": true,
"requires": {
"cosmiconfig": "^7.0.0",
"klona": "^2.0.4",
"semver": "^7.3.4"
},
"dependencies": {
"cosmiconfig": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz",
"integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==",
"dev": true,
"requires": {
"@types/parse-json": "^4.0.0",
"import-fresh": "^3.2.1",
"parse-json": "^5.0.0",
"path-type": "^4.0.0",
"yaml": "^1.10.0"
}
},
"semver": {
"version": "7.3.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz",
"integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
}
}
},
"postcss-merge-longhand": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz",
@ -10559,6 +10892,16 @@
"integrity": "sha512-icoIPxDpOo/q7SUCHSW152dCr83z7QS/6s2V3phweKu1bfJcXSObVAq/Z8OeSX7ykuXrcV2UpZbfljRI2rIOMg==",
"dev": true
},
"pretty-error": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.2.tgz",
"integrity": "sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==",
"dev": true,
"requires": {
"lodash": "^4.17.20",
"renderkid": "^2.0.4"
}
},
"pretty-format": {
"version": "26.6.2",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
@ -10882,12 +11225,48 @@
}
}
},
"relateurl": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
"integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
"dev": true
},
"remove-trailing-separator": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
"integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
"dev": true
},
"renderkid": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/renderkid/-/renderkid-2.0.5.tgz",
"integrity": "sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ==",
"dev": true,
"requires": {
"css-select": "^2.0.2",
"dom-converter": "^0.2",
"htmlparser2": "^3.10.1",
"lodash": "^4.17.20",
"strip-ansi": "^3.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"requires": {
"ansi-regex": "^2.0.0"
}
}
}
},
"repeat-element": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
@ -12582,6 +12961,12 @@
"object.getownpropertydescriptors": "^2.1.0"
}
},
"utila": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
"integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
"dev": true
},
"utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@ -13117,6 +13502,12 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"dev": true
},
"yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",

View File

@ -3,9 +3,9 @@
"version": "3.0.0",
"description": "On-screen Javascript Virtual Keyboard",
"main": "build/index.js",
"types": "build/index.d.ts",
"types": "build/types/index.d.ts",
"scripts": {
"start": "webpack serve",
"start": "webpack serve --config webpack.config.demo.js",
"build": "webpack && tsc",
"test": "jest --silent",
"coverage": "npm run test -- --coverage",
@ -45,17 +45,23 @@
"@babel/plugin-transform-typescript": "^7.13.0",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.13.9",
"@types/jest": "^26.0.20",
"@typescript-eslint/eslint-plugin": "^4.16.1",
"@typescript-eslint/parser": "^4.16.1",
"autoprefixer": "^10.2.5",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.2.2",
"babel-preset-minify": "^0.5.0",
"core-js": "^3.9.1",
"css-loader": "^5.1.1",
"eslint": "^7.21.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.3.0",
"jest": "^26.6.3",
"mini-css-extract-plugin": "^1.3.9",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"postcss": "^8.2.8",
"postcss-loader": "^5.2.0",
"prettier": "^2.2.1",
"prettier-webpack-plugin": "^1.2.0",
"style-loader": "^2.0.0",
@ -74,6 +80,7 @@
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts",
"!src/lib/index.js",
"!src/lib/polyfills.js",
"!src/demo/index.js",
"!src/utils/**",
"!src/**/*.d.ts",

View File

@ -2,7 +2,7 @@ import Keyboard from "../lib";
import "./css/BasicDemo.css";
const setDOM = () => {
document.querySelector("#root").innerHTML = `
document.querySelector("body").innerHTML = `
<input class="input" placeholder="Tap on the virtual keyboard to start" />
<div class="simple-keyboard"></div>
`;

View File

@ -2,7 +2,7 @@ import Keyboard from "../lib";
import "./css/ButtonThemeDemo.css";
const setDOM = () => {
document.querySelector("#root").innerHTML = `
document.querySelector("body").innerHTML = `
<input class="input" placeholder="Tap on the virtual keyboard to start" />
<div class="simple-keyboard"></div>
`;

View File

@ -4,7 +4,7 @@ import "./css/DOMElementDemo.css";
class Demo {
constructor() {
const rootDOM =
document.querySelector("#root") || document.createElement("div");
document.querySelector("body") || document.createElement("div");
const keyboard1DOM = document.createElement("div");
keyboard1DOM.className = "my-keyboard";

View File

@ -2,7 +2,7 @@ import Keyboard from "../lib";
import "./css/FullKeyboardDemo.css";
const setDOM = () => {
document.querySelector("#root").innerHTML = `
document.querySelector("body").innerHTML = `
<input class="input" placeholder="Tap on the virtual keyboard to start" />
<div class="keyboardContainer">
<div class="simple-keyboard-main"></div>

View File

@ -2,7 +2,7 @@ import Keyboard from "../lib";
import "./css/MultipleKeyboardsDestroyDemo.css";
const setDOM = () => {
document.querySelector("#root").innerHTML = `
document.querySelector("body").innerHTML = `
<input class="input" placeholder="Tap on the virtual keyboard to start" />
<div class="simple-keyboard"></div>

View File

@ -2,7 +2,7 @@ import Keyboard from "../lib";
import "./css/RTLDemo.css";
const setDOM = () => {
document.querySelector("#root").innerHTML = `
document.querySelector("body").innerHTML = `
<input class="input" placeholder="Tap on the virtual keyboard to start" />
<div class="simple-keyboard"></div>
`;

View File

@ -8,6 +8,11 @@
box-sizing: border-box;
overflow: hidden;
touch-action: manipulation;
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue",
Helvetica, Arial, "Lucida Grande", sans-serif;
background-color: #ececec;
padding: 5px;
border-radius: 5px;
}
.hg-theme-default .hg-button span {
@ -21,14 +26,6 @@
font-size: inherit;
}
.hg-theme-default {
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue",
Helvetica, Arial, "Lucida Grande", sans-serif;
background-color: #ececec;
padding: 5px;
border-radius: 5px;
}
.hg-theme-default .hg-button {
display: inline-block;
flex-grow: 1;
@ -74,7 +71,14 @@
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.hg-theme-default .hg-button.hg-activeButton {
.hg-theme-default .hg-button.hg-standardBtn {
width: 20px;
}
.hg-theme-default
.hg-button.hg-functionBtn
.hg-theme-default
.hg-button.hg-activeButton {
background: #efefef;
}

View File

@ -1,7 +1,17 @@
import Keyboard from '../Keyboard';
import { setDOM, clearDOM, testLayoutStdButtons, iterateButtons, triggerDocumentPointerUp, triggerDocumentPointerDown } from '../../../utils/TestUtility';
import { setDOM, clearDOM, testLayoutStdButtons, triggerDocumentPointerUp } from '../../../utils/TestUtility';
beforeEach(() => {
setDOM();
});
afterEach(() => {
clearDOM();
});
it('Keyboard will not render without target element', () => {
clearDOM();
try {
new Keyboard();
expect(true).toBe(false);
@ -10,18 +20,29 @@ it('Keyboard will not render without target element', () => {
}
});
it('Keyboard will run without options', () => {
// Prepare target DOM element
setDOM();
describe('When window is undefined', () => {
const { window } = global;
beforeAll(() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete global.window;
});
afterAll(() => {
global.window = window;
});
it('Keyboard will return early if window is undefined', () => {
const keyboard = new Keyboard();
expect(keyboard.initialized).toBeUndefined();
});
});
it('Keyboard will run without options', () => {
// No options
new Keyboard();
});
it('Keyboard will run with empty options', () => {
// Prepare target DOM element
setDOM();
// No options
new Keyboard({});
});
@ -101,9 +122,7 @@ it('Keyboard setOptions will work without a param', () => {
});
it('Keyboard empty buttons wont do anything as expected', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
layout: {
default: [
"{//} {button} d",
@ -116,9 +135,7 @@ it('Keyboard empty buttons wont do anything as expected', () => {
});
it('Keyboard onKeyPress will work', () => {
setDOM();
let pressed = false;
let pressed = false;
const keyboard = new Keyboard({
onKeyPress: () => {
@ -133,13 +150,11 @@ it('Keyboard onKeyPress will work', () => {
});
it('Keyboard standard function buttons will not change input', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
useButtonTag: true
});
iterateButtons((button) => {
keyboard.recurseButtons((button) => {
if(button.getAttribute("data-skbtn") === "{shift}"){
button.onclick();
}
@ -189,9 +204,7 @@ it('Keyboard syncInstanceInputs will work', () => {
});
it('Keyboard onChange will work', () => {
setDOM();
let output = false;
let output = false;
const keyboard = new Keyboard({
onChange: (input) => {
@ -206,9 +219,7 @@ it('Keyboard onChange will work', () => {
});
it('Keyboard onChangeAll will work', () => {
setDOM();
let output;
let output;
const keyboard = new Keyboard({
onChangeAll: (input) => {
@ -223,9 +234,7 @@ it('Keyboard onChangeAll will work', () => {
});
it('Keyboard clearInput will work', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
/**
* Avoid setInput for this test
@ -267,9 +276,7 @@ it('Keyboard clearInput will work with syncInstanceInputs', () => {
});
it('Keyboard setInput will work', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.setInput("hello");
@ -297,9 +304,7 @@ it('Keyboard setInput will work with syncInstanceInputs', () => {
});
it('Keyboard dispatch will work', () => {
setDOM();
document.body.innerHTML = `
document.body.innerHTML = `
<div class="keyboard1"></div>
<div class="keyboard2"></div>
`;
@ -322,9 +327,7 @@ it('Keyboard dispatch will work', () => {
});
it('Keyboard dispatch will not work without SimpleKeyboardInstances', () => {
setDOM();
document.body.innerHTML = `
document.body.innerHTML = `
<div class="keyboard1"></div>
<div class="keyboard2"></div>
`;
@ -353,27 +356,21 @@ it('Keyboard dispatch will not work without SimpleKeyboardInstances', () => {
});
it('Keyboard addButtonTheme will work', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.addButtonTheme("q", "test");
expect(keyboard.options.buttonTheme[0].class).toBe("test");
});
it('Keyboard addButtonTheme will not work without params', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
const returnVal = keyboard.addButtonTheme();
expect(returnVal).toBeFalsy();
});
it('Keyboard addButtonTheme will amend a buttonTheme', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
buttonTheme: [
{
class: "test",
@ -388,9 +385,7 @@ it('Keyboard addButtonTheme will amend a buttonTheme', () => {
});
it('Keyboard addButtonTheme will create a buttonTheme', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
buttonTheme: [
{
class: "blurb",
@ -405,9 +400,7 @@ it('Keyboard addButtonTheme will create a buttonTheme', () => {
});
it('Keyboard addButtonTheme will ignore a repeated buttonTheme', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
buttonTheme: [
{
class: "test",
@ -422,9 +415,7 @@ it('Keyboard addButtonTheme will ignore a repeated buttonTheme', () => {
});
it('Keyboard addButtonTheme will amend a buttonTheme', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
buttonTheme: [
{
class: "test",
@ -440,9 +431,7 @@ it('Keyboard addButtonTheme will amend a buttonTheme', () => {
it('Keyboard removeButtonTheme without params will remove all button themes', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
buttonTheme: [
{
class: "test",
@ -458,9 +447,7 @@ it('Keyboard removeButtonTheme without params will remove all button themes', ()
it('Keyboard removeButtonTheme will work', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
buttonTheme: [
{
class: "test",
@ -475,9 +462,7 @@ it('Keyboard removeButtonTheme will work', () => {
});
it('Keyboard removeButtonTheme will work wihtout a class', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
buttonTheme: [
{
class: "test",
@ -492,9 +477,7 @@ it('Keyboard removeButtonTheme will work wihtout a class', () => {
});
it('Keyboard removeButtonTheme will do nothing without a button param', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
buttonTheme: [
{
class: "test",
@ -509,9 +492,7 @@ it('Keyboard removeButtonTheme will do nothing without a button param', () => {
});
it('Keyboard removeButtonTheme does nothing if req button doesnt have a buttonTheme', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
buttonTheme: [
{
class: "test",
@ -526,9 +507,7 @@ it('Keyboard removeButtonTheme does nothing if req button doesnt have a buttonTh
});
it('Keyboard removeButtonTheme does nothing if buttonTheme class does not exist', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
buttonTheme: [
{
class: "testy",
@ -543,9 +522,7 @@ it('Keyboard removeButtonTheme does nothing if buttonTheme class does not exist'
});
it('Keyboard removeButtonTheme does nothing if buttonTheme doesnt have the requested buttons', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
buttonTheme: [
{
class: "test",
@ -560,9 +537,7 @@ it('Keyboard removeButtonTheme does nothing if buttonTheme doesnt have the reque
});
it('Keyboard getButtonElement will not return anything if empty match', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
layout: {
default: [
"{//} {button} d",
@ -575,17 +550,13 @@ it('Keyboard getButtonElement will not return anything if empty match', () => {
});
it('Keyboard getButtonElement will return multiple matched buttons', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
expect(keyboard.getButtonElement("{shift}").length).toBe(2);
});
it('Keyboard will receive physical keyboard events', () => {
setDOM();
new Keyboard({
new Keyboard({
debug: true,
physicalKeyboardHighlight: true
});
@ -602,9 +573,7 @@ it('Keyboard will receive physical keyboard events', () => {
});
it('Keyboard caretEventHandler will detect input, textarea focus', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
const myInput = document.createElement('input');
keyboard.caretEventHandler({
@ -619,9 +588,7 @@ it('Keyboard caretEventHandler will detect input, textarea focus', () => {
});
it('Keyboard caretEventHandler will not set caretPosition on disableCaretPositioning', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
const myInput = document.createElement('input');
keyboard.caretEventHandler({
@ -650,9 +617,7 @@ it('Keyboard caretEventHandler will not set caretPosition on disableCaretPositio
});
it('Keyboard caretEventHandler ignore positioning if input, textarea is blur', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.isMouseHold = true;
@ -668,9 +633,7 @@ it('Keyboard caretEventHandler ignore positioning if input, textarea is blur', (
});
it('Keyboard caretEventHandler will work with debug', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
debug: true
});
@ -693,9 +656,7 @@ it('Keyboard caretEventHandler will work with debug', () => {
});
it('Keyboard onInit will work', () => {
setDOM();
let passed = false;
let passed = false;
new Keyboard({
onInit: () => {
@ -707,9 +668,7 @@ it('Keyboard onInit will work', () => {
});
it('Keyboard onRender will work', () => {
setDOM();
let passed = false;
let passed = false;
new Keyboard({
onRender: () => {
@ -721,9 +680,7 @@ it('Keyboard onRender will work', () => {
});
it('Keyboard buttonTheme that is invalid will be ignored and not throw', () => {
setDOM();
new Keyboard({
new Keyboard({
buttonTheme: [
{
class: null,
@ -734,9 +691,7 @@ it('Keyboard buttonTheme that is invalid will be ignored and not throw', () => {
});
it('Keyboard buttonTheme buttons that are invalid will be ignored and not throw', () => {
setDOM();
new Keyboard({
new Keyboard({
buttonTheme: [
{
class: null,
@ -747,9 +702,7 @@ it('Keyboard buttonTheme buttons that are invalid will be ignored and not throw'
});
it('Keyboard buttonTheme will be ignored if buttons param not a string', () => {
setDOM();
new Keyboard({
new Keyboard({
buttonTheme: [
{
class: "test",
@ -762,9 +715,7 @@ it('Keyboard buttonTheme will be ignored if buttons param not a string', () => {
});
it('Keyboard buttonTheme will be ignored if already added', () => {
setDOM();
new Keyboard({
new Keyboard({
buttonTheme: [
{
class: "test",
@ -791,9 +742,7 @@ it('Keyboard buttonTheme will be ignored if already added', () => {
});
it('Keyboard can set a module', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.registerModule(
"test",
@ -806,9 +755,7 @@ it('Keyboard can set a module', () => {
});
it('Keyboard registerModule will return current module tree', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.modules.test = {
testy: "test"
@ -826,9 +773,7 @@ it('Keyboard registerModule will return current module tree', () => {
});
it('Keyboard can set a module by amending the modules tree', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.modules = {
testman: {
@ -847,17 +792,13 @@ it('Keyboard can set a module by amending the modules tree', () => {
});
it('Keyboard will not retrieve an option for an inexistent module', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
expect(keyboard.getModuleProp("test", "foo")).toBeFalsy();
});
it('Keyboard will get a list of modules', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.registerModule(
"test",
@ -870,9 +811,7 @@ it('Keyboard will get a list of modules', () => {
});
it('Keyboard loadModules will load a simple module', () => {
setDOM();
class myClass {
class myClass {
init = (module) => {
module.foo = "bar";
};
@ -886,9 +825,7 @@ it('Keyboard loadModules will load a simple module', () => {
});
it('Keyboard handleButtonMouseUp will set isMouseHold to false', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.isMouseHold = true;
@ -900,9 +837,7 @@ it('Keyboard handleButtonMouseUp will set isMouseHold to false', () => {
});
it('Keyboard handleButtonMouseUp clear holdInteractionTimeout', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.isMouseHold = true;
keyboard.holdInteractionTimeout = setTimeout(() => {}, 10000);
@ -913,9 +848,7 @@ it('Keyboard handleButtonMouseUp clear holdInteractionTimeout', () => {
});
it('Keyboard handleButtonMouseDown will work', () => {
setDOM();
const keyboard = new Keyboard({ useMouseEvents: true });
const keyboard = new Keyboard({ useMouseEvents: true });
console.log(keyboard.getButtonElement("q"))
keyboard.getButtonElement("q").onclick();
@ -927,9 +860,7 @@ it('Keyboard handleButtonMouseDown will work', () => {
});
it('Keyboard handleButtonMouseDown will work with preventMouseDownDefault', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
preventMouseDownDefault: true,
stopMouseDownPropagation: true
});
@ -958,9 +889,7 @@ it('Keyboard handleButtonMouseDown will work with preventMouseDownDefault', () =
});
it('Keyboard handleButtonMouseUp will work with preventMouseUpDefault and stopMouseUpPropagation', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
preventMouseUpDefault: true,
stopMouseUpPropagation: true
});
@ -987,9 +916,7 @@ it('Keyboard handleButtonMouseUp will work with preventMouseUpDefault and stopMo
});
it('Keyboard onModulesLoaded will work', () => {
setDOM();
class myClass {
class myClass {
init = (module) => {
module.foo = "bar";
};
@ -1010,9 +937,7 @@ it('Keyboard onModulesLoaded will work', () => {
});
it('Keyboard inputPattern will work globally', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
inputPattern: /^\d+$/,
useMouseEvents: true
});
@ -1027,9 +952,7 @@ it('Keyboard inputPattern will work globally', () => {
});
it('Keyboard inputPattern will work by input name', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
debug: true,
inputName: "test1",
inputPattern: {
@ -1054,9 +977,7 @@ it('Keyboard inputPattern will work by input name', () => {
});
it('Keyboard processAutoTouchEvents will work', () => {
setDOM();
navigator.maxTouchPoints = true;
navigator.maxTouchPoints = true;
const keyboard = new Keyboard({
autoUseTouchEvents: true
@ -1066,9 +987,7 @@ it('Keyboard processAutoTouchEvents will work', () => {
});
it('Keyboard processAutoTouchEvents will work with debugging enabled', () => {
setDOM();
navigator.maxTouchPoints = true;
navigator.maxTouchPoints = true;
const keyboard = new Keyboard({
autoUseTouchEvents: true,
@ -1079,9 +998,7 @@ it('Keyboard processAutoTouchEvents will work with debugging enabled', () => {
});
it('Keyboard beforeFirstRender method will work', () => {
setDOM();
let timesCalled = 0;
let timesCalled = 0;
const keyboard = new Keyboard({
beforeFirstRender: () => {
@ -1100,9 +1017,7 @@ it('Keyboard beforeFirstRender method will work', () => {
});
it('Keyboard beforeFirstRender will show PointerEvents warning', () => {
setDOM();
let timesCalled = 0;
let timesCalled = 0;
window.PointerEvent = window.PointerEvent ? window.PointerEvent : () => {};
@ -1117,9 +1032,7 @@ it('Keyboard beforeFirstRender will show PointerEvents warning', () => {
});
it('Keyboard beforeRender method will work', () => {
setDOM();
let timesCalled = 0;
let timesCalled = 0;
const keyboard = new Keyboard({
beforeRender: () => {
@ -1138,9 +1051,7 @@ it('Keyboard beforeRender method will work', () => {
});
it('Keyboard parseRowDOMContainers will work', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
layout: {
'default': [
'` [1 2 3 4 5 6 7 8 9] 0 - = {bksp}',
@ -1171,9 +1082,7 @@ it('Keyboard parseRowDOMContainers will work', () => {
});
it('Keyboard parseRowDOMContainers will ignore empty rows', () => {
setDOM();
let failed = false;
let failed = false;
try {
const keyboard = new Keyboard();
@ -1189,9 +1098,7 @@ it('Keyboard parseRowDOMContainers will ignore empty rows', () => {
it('Keyboard parseRowDOMContainers will ignore missing endIndex or endIndex before startIndex', () => {
setDOM();
new Keyboard({
new Keyboard({
layout: {
'default': [
'` [1 2 3 4 5 6 7 8 9 0 - = {bksp}',
@ -1206,9 +1113,7 @@ it('Keyboard parseRowDOMContainers will ignore missing endIndex or endIndex befo
});
it('Keyboard disableRowButtonContainers will bypass parseRowDOMContainers', () => {
setDOM();
new Keyboard({
new Keyboard({
disableRowButtonContainers: true,
layout: {
'default': [
@ -1234,9 +1139,7 @@ it('Keyboard disableRowButtonContainers will bypass parseRowDOMContainers', () =
});
it('Keyboard inputName change will trigget caretPosition reset', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.setCaretPosition(0);
@ -1257,9 +1160,7 @@ it('Keyboard inputName change will trigget caretPosition reset', () => {
});
it('Keyboard destroy will work', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.destroy();
expect(keyboard.keyboardDOM.innerHTML).toBe("");
@ -1280,17 +1181,13 @@ it('Keyboard destroy will work', () => {
});
it('Keyboard destroy will work with debug option', () => {
setDOM();
const keyboard = new Keyboard({ debug: true });
const keyboard = new Keyboard({ debug: true });
keyboard.destroy();
expect(keyboard.keyboardDOM.innerHTML).toBe("");
});
it('Keyboard disableButtonHold will work', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
disableButtonHold: true
});
@ -1298,9 +1195,7 @@ it('Keyboard disableButtonHold will work', () => {
});
it('Keyboard caretEventHandler will be triggered on mouseup and ontouchend', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
disableCaretPositioning: true
});
@ -1323,9 +1218,7 @@ it('Keyboard caretEventHandler will be triggered on mouseup and ontouchend', ()
});
it('Keyboard onKeyReleased will work', () => {
setDOM();
let pressed = false;
let pressed = false;
let firedTimes = 0;
let buttonPressed;
@ -1347,9 +1240,7 @@ it('Keyboard onKeyReleased will work', () => {
});
it('Keyboard buttonAttribute will work', () => {
setDOM();
new Keyboard({
new Keyboard({
buttonAttributes: [
{
attribute: "aria-label",
@ -1361,9 +1252,7 @@ it('Keyboard buttonAttribute will work', () => {
});
it('Keyboard buttonAttribute will warn about invalid entries', () => {
setDOM();
new Keyboard({
new Keyboard({
buttonAttributes: [
{
attribute: false,
@ -1403,16 +1292,12 @@ it('Keyboard will work with a DOM element param with class', () => {
});
it('Keyboard handleKeyboardContainerMouseDown will work', () => {
setDOM();
const keyboard = new Keyboard();
const keyboard = new Keyboard();
keyboard.keyboardDOM.onpointerdown();
});
it('Keyboard handleKeyboardContainerMouseDown will respect preventMouseDownDefault', () => {
setDOM();
let works = false;
let works = false;
const keyboard = new Keyboard({ preventMouseDownDefault: true });
keyboard.keyboardDOM.onpointerdown({ preventDefault: () => {
works = true
@ -1422,9 +1307,7 @@ it('Keyboard handleKeyboardContainerMouseDown will respect preventMouseDownDefau
});
it('Keyboard caret positioning will work', () => {
setDOM();
const keyboard = new Keyboard({
const keyboard = new Keyboard({
onKeyPress: (button) => {
if (button === "{shift}" || button === "{lock}") handleShift();
else if (keyboard.options.layoutName === "shift") handleShift();
@ -1449,4 +1332,23 @@ it('Keyboard caret positioning will work', () => {
keyboard.getButtonElement("l").onpointerdown();
expect(keyboard.getInput()).toBe("hEllo");
});
it('Keyboard excludeFromLayout will work', () => {
const keyboard = new Keyboard();
expect(keyboard.getButtonElement("a")).toBeDefined();
keyboard.setOptions({
excludeFromLayout: {
default: ["a"]
}
});
expect(keyboard.getButtonElement("a")).toBeUndefined();
});
it('Keyboard onSetOptions can be called without changedOptions param', () => {
const keyboard = new Keyboard();
expect(keyboard.onSetOptions()).toBeUndefined();
});

View File

@ -1,3 +1,7 @@
/* eslint-disable no-unused-vars */
import SimpleKeyboard from "./components/Keyboard";
import Utilities from "./services/Utilities";
export interface KeyboardLayoutObject {
[key: string]: string[];
}
@ -17,11 +21,33 @@ export interface KeyboardInput {
[key: string]: string
}
export type KeyboardButton = HTMLDivElement | HTMLButtonElement;
export type KeyboardParams = [KeyboardOptions] | [string | HTMLDivElement, KeyboardOptions];
export type CandidateBoxParams = {
utilities: Utilities
}
export type CandidateBoxShowParams = {
candidateValue: string,
targetElement: KeyboardElement,
// eslint-disable-next-line no-unused-vars
onSelect: (selectedCandidate: string) => void
}
export type CandidateBoxRenderParams = {
candidateListPages: string[][],
targetElement: KeyboardElement,
pageIndex: number;
nbPages: number;
// eslint-disable-next-line no-unused-vars
onItemSelected: (selectedCandidate: string) => void
}
export type KeyboardElement = HTMLDivElement | HTMLButtonElement;
export type KeyboardHandlerEvent = PointerEvent & TouchEvent & KeyboardEvent & { target: HTMLDivElement | HTMLInputElement };
export interface KeyboardButtonElements {
[key: string]: KeyboardButton[]
[key: string]: KeyboardElement[]
}
export interface UtilitiesParams {
@ -179,6 +205,36 @@ export interface KeyboardOptions {
*/
rtl?: boolean;
/**
* Enable input method editor candidate list support.
*/
enableLayoutCandidates?: boolean;
/**
* Character suggestions to be shown on certain key presses
*/
layoutCandidates?: { [key: string]: string };
/**
* Exclude buttons from layout
*/
excludeFromLayout?: { [key: string]: string[] };
/**
* Determine size of layout candidate list
*/
layoutCandidatesPageSize?: number;
/**
* Executes the callback function every time simple-keyboard is rendered (e.g: when you change layouts).
*/
onRender?: (instance?: SimpleKeyboard) => void;
/**
* Executes the callback function once simple-keyboard is rendered for the first time (on initialization).
*/
onInit?: (instance?: SimpleKeyboard) => void;
/**
* Module options can have any format
*/

View File

@ -31,17 +31,26 @@ class Utilities {
Utilities.bindMethods(Utilities, this);
}
/**
* Retrieve button type
*
* @param {string} button The button's layout name
* @return {string} The button type
*/
getButtonType(button: string): string {
return button.includes("{") && button.includes("}") && button !== "{//}"
? "functionBtn"
: "standardBtn";
}
/**
* Adds default classes to a given button
*
* @param {string} button The button's layout name
* @return {string} The classes to be added to the button
*/
getButtonClass(button: string) {
const buttonTypeClass =
button.includes("{") && button.includes("}") && button !== "{//}"
? "functionBtn"
: "standardBtn";
getButtonClass(button: string): string {
const buttonTypeClass = this.getButtonType(button);
const buttonWithoutBraces = button.replace("{", "").replace("}", "");
let buttonNormalized = "";
@ -418,7 +427,7 @@ class Utilities {
* Determines whether pointer events are supported
*/
pointerEventsSupported() {
return window.PointerEvent;
return !!window.PointerEvent;
}
/**
@ -453,6 +462,18 @@ class Utilities {
);
}
/**
* Split array into chunks
*/
chunkArray<T>(arr: T[], size: number): T[][] {
return [...Array(Math.ceil(arr.length / size))].map((_, i) =>
arr.slice(size * i, size + size * i)
);
}
/**
* Reusable empty function
*/
static noop = () => {};
}

View File

@ -17,11 +17,11 @@ it('Keyboard mergeDisplay will work', () => {
it('Keyboard function buttons will work', () => {
setDOM();
new Keyboard();
const keyboard = new Keyboard();
testLayoutFctButtons((fctBtnCount, fctBtnHasOnclickCount) => {
testLayoutFctButtons(keyboard, (fctBtnCount, fctBtnHasOnclickCount) => {
expect(fctBtnCount).toBe(fctBtnHasOnclickCount);
});
}, keyboard);
});
it('Keyboard {bksp} button will work', () => {

View File

@ -44,7 +44,7 @@
let stdBtnCount = 0;
let fullInput = '';
iterateButtons((button) => {
keyboard.recurseButtons((button) => {
const label = button.getAttribute("data-skbtn");
if(label.includes("{"))
@ -89,11 +89,11 @@
/**
* Test if function buttons are interactive (have an onclick)
*/
export const testLayoutFctButtons = (callback) => {
export const testLayoutFctButtons = (keyboard, callback) => {
let fctBtnCount = 0;
let fctBtnHasOnclickCount = 0;
iterateButtons((button) => {
keyboard.recurseButtons((button) => {
const label = button.getAttribute("data-skbtn");
if(!label.includes("{") && !label.includes("}"))
@ -110,19 +110,6 @@
});
}
/**
* Iterates on the keyboard buttons
*/
export const iterateButtons = (callback, selector) => {
const rows = document.body.querySelector(selector || '.simple-keyboard').children;
Array.from(rows).forEach(row => {
Array.from(row.children).forEach((button) => {
callback(button);
});
});
}
/**
* Remove RTL control chars
*/

View File

@ -1,7 +1,7 @@
{
"compilerOptions": {
"noImplicitAny": true,
"outFile": "build/index.d.ts",
"outDir": "build/types",
"module": "esnext",
"target": "es5",
"allowJs": true,
@ -9,9 +9,10 @@
"declaration": true,
"emitDeclarationOnly": true,
"suppressImplicitAnyIndexErrors": true,
"lib": ["es2018", "dom"],
"lib": ["es2020", "dom"],
"moduleResolution": "node",
"downlevelIteration": true
},
"include": ["src/lib"],
"exclude": ["src/**/tests"],
"exclude": ["src/lib/**/tests"],
}

51
webpack.config.demo.js Normal file
View File

@ -0,0 +1,51 @@
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: "development",
devtool: 'cheap-module-source-map',
entry: './src/demo/index.js',
output: {
filename: 'index.js'
},
optimization: {
minimize: false,
},
devServer: {
open: true,
hot: true,
host: "localhost",
port: 9000
},
module: {
rules: [
{
test: /\.(m|j|t)s$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader'
}
},
{
test: /\.(sa|sc|c)ss$/,
use: [
MiniCssExtractPlugin.loader,
{ loader: "css-loader", options: { sourceMap: true } },
],
},
{
test: /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/,
use: ['url-loader'],
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/index.css'
}),
new HtmlWebpackPlugin(),
],
resolve: {
extensions: ['.ts', '.js', '.json']
}
};