mirror of
https://github.com/hodgef/simple-keyboard.git
synced 2025-03-04 00:12:58 +08:00
CandidateBox, IE support
This commit is contained in:
parent
cae07749f4
commit
82abc2b3ee
38
README.md
38
README.md
@ -31,31 +31,33 @@
|
|||||||
<blockquote>The slick virtual keyboard for Javascript. Compatible with your JS, React, Angular or Vue projects.</blockquote>
|
<blockquote>The slick virtual keyboard for Javascript. Compatible with your JS, React, Angular or Vue projects.</blockquote>
|
||||||
|
|
||||||
## Demo 🚀
|
## Demo 🚀
|
||||||
|
|
||||||
[Demo Showcase (Vanilla, Angular, React, Vue)](https://simple-keyboard.com/demo)
|
[Demo Showcase (Vanilla, Angular, React, Vue)](https://simple-keyboard.com/demo)
|
||||||
|
|
||||||
## Installation & Usage 📦
|
## Installation & Usage 📦
|
||||||
|
|
||||||
You can use simple-keyboard as a `<script>` tag from a CDN, or install it from npm.
|
You can use simple-keyboard as a `<script>` tag from a CDN, or install it from npm.
|
||||||
|
|
||||||
Check out the [Getting Started](https://simple-keyboard.com/getting-started) docs to begin.
|
Check out the [Getting Started](https://simple-keyboard.com/getting-started) docs to begin.
|
||||||
|
|
||||||
## Documentation 📖
|
## Documentation 📖
|
||||||
|
|
||||||
Check out the [simple-keyboard documentation](https://simple-keyboard.com/documentation) site.
|
Check out the [simple-keyboard documentation](https://simple-keyboard.com/documentation) site.
|
||||||
|
|
||||||
Feel free to browse the [Questions & Answers (FAQ)](https://simple-keyboard.com/qa-use-cases/) page for common use-cases.
|
Feel free to browse the [Questions & Answers (FAQ)](https://simple-keyboard.com/qa-use-cases/) page for common use-cases.
|
||||||
|
|
||||||
|
|
||||||
### To run demo on your own computer
|
### To run demo on your own computer
|
||||||
|
|
||||||
* Clone this repository
|
- Clone this repository
|
||||||
* `npm install`
|
- `npm install`
|
||||||
* `npm start`
|
- `npm start`
|
||||||
* Visit [http://localhost:3000/](http://localhost:3000/)
|
- Visit [http://localhost:3000/](http://localhost:3000/)
|
||||||
|
|
||||||
### Other versions
|
### Other versions
|
||||||
|
|
||||||
* [React.js](https://github.com/hodgef/react-simple-keyboard)
|
- [React.js](https://github.com/hodgef/react-simple-keyboard)
|
||||||
* [Angular](https://simple-keyboard.com/demo)
|
- [Angular](https://simple-keyboard.com/demo)
|
||||||
* [Vue.js](https://simple-keyboard.com/demo)
|
- [Vue.js](https://simple-keyboard.com/demo)
|
||||||
|
|
||||||
### Questions? Join the chat
|
### Questions? Join the chat
|
||||||
|
|
||||||
@ -65,19 +67,21 @@ Feel free to browse the [Questions & Answers (FAQ)](https://simple-keyboard.com/
|
|||||||
|
|
||||||
You can extend simple-keyboard's functionality with [modules](https://hodgef.com/simple-keyboard/modules/). Such as:
|
You can extend simple-keyboard's functionality with [modules](https://hodgef.com/simple-keyboard/modules/). Such as:
|
||||||
|
|
||||||
* [Autocorrect](https://hodgef.com/simple-keyboard/modules/autocorrect/)
|
- [Autocorrect](https://hodgef.com/simple-keyboard/modules/autocorrect/)
|
||||||
* [Input Mask](https://hodgef.com/simple-keyboard/modules/input-mask/)
|
- [Input Mask](https://hodgef.com/simple-keyboard/modules/input-mask/)
|
||||||
* [Key Navigation](https://hodgef.com/simple-keyboard/modules/key-navigation/)
|
- [Key Navigation](https://hodgef.com/simple-keyboard/modules/key-navigation/)
|
||||||
* [Swipe Keyboard](https://hodgef.com/simple-keyboard/modules/swipe-keyboard/)
|
- [Swipe Keyboard](https://hodgef.com/simple-keyboard/modules/swipe-keyboard/)
|
||||||
|
|
||||||
Want to create your own module? Check out the [Modules page](https://hodgef.com/simple-keyboard/modules/) for instructions.
|
Want to create your own module? Check out the [Modules page](https://hodgef.com/simple-keyboard/modules/) for instructions.
|
||||||
|
|
||||||
## Compatibility 🎯
|
## Compatibility 🎯
|
||||||
|
- Internet Explorer 11
|
||||||
> Simple-keyboard is intended for modern, standards-compliant browsers.
|
- Edge (Spartan) 16+
|
||||||
> Internet Explorer is sadly not one of them, and since its market-share is negligible (~2% for IE11), resources won't be spent in trying to support it.
|
- Edge (Anaheim/Edge Chromium) 79+
|
||||||
>
|
- Chrome 49+
|
||||||
> For more information, check out [this Q&A entry](https://hodgef.com/simple-keyboard/questions-answers/internet-explorer-not-supported/).
|
- Safari 9+
|
||||||
|
- Firefox 57+
|
||||||
|
- iOS 9+
|
||||||
|
|
||||||
## Contributing ✅
|
## Contributing ✅
|
||||||
|
|
||||||
|
@ -1,6 +1,19 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: [
|
presets: [
|
||||||
["@babel/env"]
|
["@babel/env", {
|
||||||
|
corejs:"3",
|
||||||
|
useBuiltIns: 'usage',
|
||||||
|
"targets": {
|
||||||
|
"browsers": [
|
||||||
|
"edge >= 16",
|
||||||
|
"safari >= 9",
|
||||||
|
"firefox >= 57",
|
||||||
|
"ie >= 11",
|
||||||
|
"ios >= 9",
|
||||||
|
"chrome >= 49"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
}]
|
||||||
],
|
],
|
||||||
plugins: [
|
plugins: [
|
||||||
["@babel/plugin-proposal-class-properties"],
|
["@babel/plugin-proposal-class-properties"],
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*!
|
/*!
|
||||||
*
|
*
|
||||||
* simple-keyboard v3.0.0-beta.0
|
* simple-keyboard v3.0.0-beta.1
|
||||||
* https://github.com/hodgef/simple-keyboard
|
* https://github.com/hodgef/simple-keyboard
|
||||||
*
|
*
|
||||||
* Copyright (c) Francisco Hodge (https://github.com/hodgef) and project contributors.
|
* Copyright (c) Francisco Hodge (https://github.com/hodgef) and project contributors.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*!
|
/*!
|
||||||
*
|
*
|
||||||
* simple-keyboard v3.0.0-beta.0
|
* simple-keyboard v3.0.0-beta.1
|
||||||
* https://github.com/hodgef/simple-keyboard
|
* https://github.com/hodgef/simple-keyboard
|
||||||
*
|
*
|
||||||
* Copyright (c) Francisco Hodge (https://github.com/hodgef) and project contributors.
|
* Copyright (c) Francisco Hodge (https://github.com/hodgef) and project contributors.
|
||||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "simple-keyboard",
|
"name": "simple-keyboard",
|
||||||
"version": "3.0.0-beta.0",
|
"version": "3.0.0-beta.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
129
src/lib/components/CandidateBox.ts
Normal file
129
src/lib/components/CandidateBox.ts
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import "./css/CandidateBox.css";
|
||||||
|
|
||||||
|
import Utilities from "../services/Utilities";
|
||||||
|
import {
|
||||||
|
CandidateBoxParams,
|
||||||
|
CandidateBoxRenderParams,
|
||||||
|
CandidateBoxShowParams,
|
||||||
|
} from "./../interfaces";
|
||||||
|
|
||||||
|
class CandidateBox {
|
||||||
|
utilities: Utilities;
|
||||||
|
candidateBoxElement: HTMLDivElement;
|
||||||
|
pageIndex = 0;
|
||||||
|
pageSize;
|
||||||
|
|
||||||
|
constructor({ utilities }: CandidateBoxParams) {
|
||||||
|
this.utilities = utilities;
|
||||||
|
Utilities.bindMethods(CandidateBox, this);
|
||||||
|
this.pageSize = this.utilities.getOptions().layoutCandidatesPageSize || 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
if (this.candidateBoxElement) {
|
||||||
|
this.candidateBoxElement.remove();
|
||||||
|
this.pageIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show({
|
||||||
|
candidateValue,
|
||||||
|
targetElement,
|
||||||
|
onSelect,
|
||||||
|
}: CandidateBoxShowParams): void {
|
||||||
|
if (!candidateValue || !candidateValue.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const candidateListPages = this.utilities.chunkArray(
|
||||||
|
candidateValue.split(" "),
|
||||||
|
this.pageSize
|
||||||
|
);
|
||||||
|
|
||||||
|
this.renderPage({
|
||||||
|
candidateListPages,
|
||||||
|
targetElement,
|
||||||
|
pageIndex: this.pageIndex,
|
||||||
|
nbPages: candidateListPages.length,
|
||||||
|
onItemSelected: (selectedCandidate: string) => {
|
||||||
|
onSelect(selectedCandidate);
|
||||||
|
this.destroy();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPage({
|
||||||
|
candidateListPages,
|
||||||
|
targetElement,
|
||||||
|
pageIndex,
|
||||||
|
nbPages,
|
||||||
|
onItemSelected,
|
||||||
|
}: CandidateBoxRenderParams) {
|
||||||
|
// Remove current candidate box, if any
|
||||||
|
this.candidateBoxElement?.remove();
|
||||||
|
|
||||||
|
// Create candidate box element
|
||||||
|
this.candidateBoxElement = document.createElement("div");
|
||||||
|
this.candidateBoxElement.className = "hg-candidate-box";
|
||||||
|
|
||||||
|
// Candidate box list
|
||||||
|
const candidateListULElement = document.createElement("ul");
|
||||||
|
candidateListULElement.className = "hg-candidate-box-list";
|
||||||
|
|
||||||
|
// Create Candidate box list items
|
||||||
|
candidateListPages[pageIndex].forEach((candidateListItem) => {
|
||||||
|
const candidateListLIElement = document.createElement("li");
|
||||||
|
candidateListLIElement.className = "hg-candidate-box-list-item";
|
||||||
|
candidateListLIElement.textContent = candidateListItem;
|
||||||
|
candidateListLIElement.onclick = () => onItemSelected(candidateListItem);
|
||||||
|
|
||||||
|
// Append list item to ul
|
||||||
|
candidateListULElement.appendChild(candidateListLIElement);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add previous button
|
||||||
|
const isPrevBtnElementActive = pageIndex > 0;
|
||||||
|
const prevBtnElement = document.createElement("div");
|
||||||
|
prevBtnElement.classList.add("hg-candidate-box-prev");
|
||||||
|
isPrevBtnElementActive &&
|
||||||
|
prevBtnElement.classList.add("hg-candidate-box-btn-active");
|
||||||
|
prevBtnElement.onclick = () => {
|
||||||
|
if (!isPrevBtnElementActive) return;
|
||||||
|
this.renderPage({
|
||||||
|
candidateListPages,
|
||||||
|
targetElement,
|
||||||
|
pageIndex: pageIndex - 1,
|
||||||
|
nbPages,
|
||||||
|
onItemSelected,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.candidateBoxElement.appendChild(prevBtnElement);
|
||||||
|
|
||||||
|
// Add elements to container
|
||||||
|
this.candidateBoxElement.appendChild(candidateListULElement);
|
||||||
|
|
||||||
|
// Add next button
|
||||||
|
const isNextBtnElementActive = pageIndex < nbPages - 1;
|
||||||
|
const nextBtnElement = document.createElement("div");
|
||||||
|
nextBtnElement.classList.add("hg-candidate-box-next");
|
||||||
|
isNextBtnElementActive &&
|
||||||
|
nextBtnElement.classList.add("hg-candidate-box-btn-active");
|
||||||
|
|
||||||
|
nextBtnElement.onclick = () => {
|
||||||
|
if (!isNextBtnElementActive) return;
|
||||||
|
this.renderPage({
|
||||||
|
candidateListPages,
|
||||||
|
targetElement,
|
||||||
|
pageIndex: pageIndex + 1,
|
||||||
|
nbPages,
|
||||||
|
onItemSelected,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
this.candidateBoxElement.appendChild(nextBtnElement);
|
||||||
|
|
||||||
|
// Append candidate box to target element
|
||||||
|
targetElement.prepend(this.candidateBoxElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CandidateBox;
|
@ -1,4 +1,4 @@
|
|||||||
import "./Keyboard.css";
|
import "./css/Keyboard.css";
|
||||||
|
|
||||||
// Services
|
// Services
|
||||||
import { getDefaultLayout } from "../services/KeyboardLayout";
|
import { getDefaultLayout } from "../services/KeyboardLayout";
|
||||||
@ -9,11 +9,13 @@ import {
|
|||||||
KeyboardInput,
|
KeyboardInput,
|
||||||
KeyboardButtonElements,
|
KeyboardButtonElements,
|
||||||
KeyboardHandlerEvent,
|
KeyboardHandlerEvent,
|
||||||
KeyboardButton,
|
KeyboardElement,
|
||||||
|
KeyboardParams,
|
||||||
} from "../interfaces";
|
} from "../interfaces";
|
||||||
|
import CandidateBox from "./CandidateBox";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Root class for simple-keyboard
|
* Root class for simple-keyboard.
|
||||||
* This class:
|
* This class:
|
||||||
* - Parses the options
|
* - Parses the options
|
||||||
* - Renders the rows and buttons
|
* - Renders the rows and buttons
|
||||||
@ -25,7 +27,7 @@ class SimpleKeyboard {
|
|||||||
utilities: any;
|
utilities: any;
|
||||||
caretPosition: number;
|
caretPosition: number;
|
||||||
caretPositionEnd: number;
|
caretPositionEnd: number;
|
||||||
keyboardDOM: KeyboardButton;
|
keyboardDOM: KeyboardElement;
|
||||||
keyboardPluginClasses: string;
|
keyboardPluginClasses: string;
|
||||||
keyboardDOMClass: string;
|
keyboardDOMClass: string;
|
||||||
buttonElements: KeyboardButtonElements;
|
buttonElements: KeyboardButtonElements;
|
||||||
@ -40,12 +42,16 @@ class SimpleKeyboard {
|
|||||||
holdTimeout: number;
|
holdTimeout: number;
|
||||||
isMouseHold: boolean;
|
isMouseHold: boolean;
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
|
candidateBox: CandidateBox;
|
||||||
|
keyboardRowsDOM: KeyboardElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance of SimpleKeyboard
|
* 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.
|
* @param {Array} params If first parameter is a string, it is considered the container class. The second parameter is then considered the options object. If first parameter is an object, it is considered the options object.
|
||||||
*/
|
*/
|
||||||
constructor(...params: []) {
|
constructor(...params: KeyboardParams) {
|
||||||
|
if (typeof window === "undefined") return;
|
||||||
|
|
||||||
const { keyboardDOMClass, keyboardDOM, options = {} } = this.handleParams(
|
const { keyboardDOMClass, keyboardDOM, options = {} } = this.handleParams(
|
||||||
params
|
params
|
||||||
);
|
);
|
||||||
@ -115,13 +121,19 @@ class SimpleKeyboard {
|
|||||||
* @property {boolean} rtl Adds unicode right-to-left control characters to input return values.
|
* @property {boolean} rtl Adds unicode right-to-left control characters to input return values.
|
||||||
* @property {function} onKeyReleased Executes the callback function on key release.
|
* @property {function} onKeyReleased Executes the callback function on key release.
|
||||||
* @property {array} modules Module classes to be loaded by simple-keyboard.
|
* @property {array} modules Module classes to be loaded by simple-keyboard.
|
||||||
|
* @property {boolean} enableLayoutCandidates Enable input method editor candidate list support.
|
||||||
|
* @property {object} excludeFromLayout Buttons to exclude from layout
|
||||||
|
* @property {number} layoutCandidatesPageSize Determine size of layout candidate list
|
||||||
*/
|
*/
|
||||||
this.options = options;
|
this.options = {
|
||||||
this.options.layoutName = this.options.layoutName || "default";
|
layoutName: "default",
|
||||||
this.options.theme = this.options.theme || "hg-theme-default";
|
theme: "hg-theme-default",
|
||||||
this.options.inputName = this.options.inputName || "default";
|
inputName: "default",
|
||||||
this.options.preventMouseDownDefault =
|
preventMouseDownDefault: false,
|
||||||
this.options.preventMouseDownDefault || false;
|
enableLayoutCandidates: true,
|
||||||
|
excludeFromLayout: {},
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {object} Classes identifying loaded plugins
|
* @type {object} Classes identifying loaded plugins
|
||||||
@ -186,6 +198,15 @@ class SimpleKeyboard {
|
|||||||
getOptions: this.getOptions,
|
getOptions: this.getOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializing CandidateBox
|
||||||
|
*/
|
||||||
|
this.candidateBox = this.options.enableLayoutCandidates
|
||||||
|
? new CandidateBox({
|
||||||
|
utilities: this.utilities,
|
||||||
|
})
|
||||||
|
: null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rendering keyboard
|
* Rendering keyboard
|
||||||
*/
|
*/
|
||||||
@ -206,10 +227,10 @@ class SimpleKeyboard {
|
|||||||
* parseParams
|
* parseParams
|
||||||
*/
|
*/
|
||||||
handleParams = (
|
handleParams = (
|
||||||
params: any[]
|
params: KeyboardParams
|
||||||
): {
|
): {
|
||||||
keyboardDOMClass: string;
|
keyboardDOMClass: string;
|
||||||
keyboardDOM: KeyboardButton;
|
keyboardDOM: KeyboardElement;
|
||||||
options: Partial<KeyboardOptions>;
|
options: Partial<KeyboardOptions>;
|
||||||
} => {
|
} => {
|
||||||
let keyboardDOMClass;
|
let keyboardDOMClass;
|
||||||
@ -224,11 +245,11 @@ class SimpleKeyboard {
|
|||||||
keyboardDOMClass = params[0].split(".").join("");
|
keyboardDOMClass = params[0].split(".").join("");
|
||||||
keyboardDOM = document.querySelector(
|
keyboardDOM = document.querySelector(
|
||||||
`.${keyboardDOMClass}`
|
`.${keyboardDOMClass}`
|
||||||
) as KeyboardButton;
|
) as KeyboardElement;
|
||||||
options = params[1];
|
options = params[1];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If first parameter is an KeyboardButton
|
* If first parameter is an KeyboardElement
|
||||||
* Consider it as the keyboard DOM element
|
* Consider it as the keyboard DOM element
|
||||||
*/
|
*/
|
||||||
} else if (params[0] instanceof HTMLDivElement) {
|
} else if (params[0] instanceof HTMLDivElement) {
|
||||||
@ -251,7 +272,7 @@ class SimpleKeyboard {
|
|||||||
keyboardDOMClass = "simple-keyboard";
|
keyboardDOMClass = "simple-keyboard";
|
||||||
keyboardDOM = document.querySelector(
|
keyboardDOM = document.querySelector(
|
||||||
`.${keyboardDOMClass}`
|
`.${keyboardDOMClass}`
|
||||||
) as KeyboardButton;
|
) as KeyboardElement;
|
||||||
options = params[0];
|
options = params[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,11 +300,85 @@ class SimpleKeyboard {
|
|||||||
this.caretPositionEnd = endPosition;
|
this.caretPositionEnd = endPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the candidates for a given input
|
||||||
|
* @param input The input string to check
|
||||||
|
*/
|
||||||
|
getInputCandidates(
|
||||||
|
input: string
|
||||||
|
): { candidateKey: string; candidateValue: string } | Record<string, never> {
|
||||||
|
const { layoutCandidates: layoutCandidatesObj } = this.options;
|
||||||
|
|
||||||
|
if (!layoutCandidatesObj || typeof layoutCandidatesObj !== "object") {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const layoutCandidates = Object.keys(layoutCandidatesObj).filter(
|
||||||
|
(layoutCandidate: string) => {
|
||||||
|
const regexp = new RegExp(`${layoutCandidate}$`, "g");
|
||||||
|
const matches = [...input.matchAll(regexp)];
|
||||||
|
return !!matches.length;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (layoutCandidates.length > 1) {
|
||||||
|
const candidateKey = layoutCandidates.sort(
|
||||||
|
(a, b) => b.length - a.length
|
||||||
|
)[0];
|
||||||
|
return {
|
||||||
|
candidateKey,
|
||||||
|
candidateValue: layoutCandidatesObj[candidateKey],
|
||||||
|
};
|
||||||
|
} else if (layoutCandidates.length) {
|
||||||
|
const candidateKey = layoutCandidates[0];
|
||||||
|
return {
|
||||||
|
candidateKey,
|
||||||
|
candidateValue: layoutCandidatesObj[candidateKey],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
if (this.candidateBox) {
|
||||||
|
this.candidateBox.show({
|
||||||
|
candidateValue,
|
||||||
|
targetElement,
|
||||||
|
onSelect: (selectedCandidate: string) => {
|
||||||
|
const currentInput = this.getInput(this.options.inputName, true);
|
||||||
|
const regexp = new RegExp(`${candidateKey}$`, "g");
|
||||||
|
const newInput = currentInput.replace(regexp, selectedCandidate);
|
||||||
|
|
||||||
|
this.setInput(newInput, this.options.inputName, true);
|
||||||
|
|
||||||
|
if (typeof this.options.onChange === "function")
|
||||||
|
this.options.onChange(this.getInput(this.options.inputName, true));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calling onChangeAll
|
||||||
|
*/
|
||||||
|
if (typeof this.options.onChangeAll === "function")
|
||||||
|
this.options.onChangeAll(this.getAllInputs());
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles clicks made to keyboard buttons
|
* Handles clicks made to keyboard buttons
|
||||||
* @param {string} button The button's layout name.
|
* @param {string} button The button's layout name.
|
||||||
*/
|
*/
|
||||||
handleButtonClicked(button: string): void {
|
handleButtonClicked(button: string, e?: KeyboardHandlerEvent): void {
|
||||||
const debug = this.options.debug;
|
const debug = this.options.debug;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -292,14 +387,14 @@ class SimpleKeyboard {
|
|||||||
if (button === "{//}") return;
|
if (button === "{//}") return;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calling onKeyPress
|
* Creating inputName if it doesn't exist
|
||||||
*/
|
*/
|
||||||
if (typeof this.options.onKeyPress === "function")
|
|
||||||
this.options.onKeyPress(button);
|
|
||||||
|
|
||||||
if (!this.input[this.options.inputName])
|
if (!this.input[this.options.inputName])
|
||||||
this.input[this.options.inputName] = "";
|
this.input[this.options.inputName] = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculating new input
|
||||||
|
*/
|
||||||
const updatedInput = this.utilities.getUpdatedInput(
|
const updatedInput = this.utilities.getUpdatedInput(
|
||||||
button,
|
button,
|
||||||
this.input[this.options.inputName],
|
this.input[this.options.inputName],
|
||||||
@ -307,6 +402,12 @@ class SimpleKeyboard {
|
|||||||
this.caretPositionEnd
|
this.caretPositionEnd
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calling onKeyPress
|
||||||
|
*/
|
||||||
|
if (typeof this.options.onKeyPress === "function")
|
||||||
|
this.options.onKeyPress(button);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
// If input will change as a result of this button press
|
// If input will change as a result of this button press
|
||||||
this.input[this.options.inputName] !== updatedInput &&
|
this.input[this.options.inputName] !== updatedInput &&
|
||||||
@ -326,7 +427,10 @@ class SimpleKeyboard {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.input[this.options.inputName] = this.utilities.getUpdatedInput(
|
/**
|
||||||
|
* Updating input
|
||||||
|
*/
|
||||||
|
const newInputValue = this.utilities.getUpdatedInput(
|
||||||
button,
|
button,
|
||||||
this.input[this.options.inputName],
|
this.input[this.options.inputName],
|
||||||
this.caretPosition,
|
this.caretPosition,
|
||||||
@ -334,6 +438,8 @@ class SimpleKeyboard {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.setInput(newInputValue, this.options.inputName, true);
|
||||||
|
|
||||||
if (debug) console.log("Input changed:", this.getAllInputs());
|
if (debug) console.log("Input changed:", this.getAllInputs());
|
||||||
|
|
||||||
if (this.options.debug) {
|
if (this.options.debug) {
|
||||||
@ -361,6 +467,25 @@ class SimpleKeyboard {
|
|||||||
*/
|
*/
|
||||||
if (typeof this.options.onChangeAll === "function")
|
if (typeof this.options.onChangeAll === "function")
|
||||||
this.options.onChangeAll(this.getAllInputs());
|
this.options.onChangeAll(this.getAllInputs());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this new input has candidates (suggested words)
|
||||||
|
*/
|
||||||
|
if (e?.target && this.options.enableLayoutCandidates) {
|
||||||
|
const { candidateKey, candidateValue } = this.getInputCandidates(
|
||||||
|
updatedInput
|
||||||
|
);
|
||||||
|
|
||||||
|
if (candidateKey && candidateValue) {
|
||||||
|
this.showCandidatesBox(
|
||||||
|
candidateKey,
|
||||||
|
candidateValue,
|
||||||
|
this.keyboardDOM
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.candidateBox.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
@ -430,8 +555,27 @@ class SimpleKeyboard {
|
|||||||
/**
|
/**
|
||||||
* Handle event options
|
* Handle event options
|
||||||
*/
|
*/
|
||||||
if (this.options.preventMouseUpDefault) e.preventDefault();
|
if (this.options.preventMouseUpDefault && e.preventDefault)
|
||||||
if (this.options.stopMouseUpPropagation) e.stopPropagation();
|
e.preventDefault();
|
||||||
|
if (this.options.stopMouseUpPropagation && e.stopPropagation)
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
const isKeyboard =
|
||||||
|
e.target === this.keyboardDOM ||
|
||||||
|
(e.target && this.keyboardDOM.contains(e.target)) ||
|
||||||
|
(this.candidateBox &&
|
||||||
|
this.candidateBox.candidateBoxElement &&
|
||||||
|
(e.target === this.candidateBox.candidateBoxElement ||
|
||||||
|
(e.target &&
|
||||||
|
this.candidateBox.candidateBoxElement.contains(e.target))));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On click outside, remove candidateBox
|
||||||
|
*/
|
||||||
|
if (!isKeyboard && this.candidateBox) {
|
||||||
|
this.candidateBox.destroy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -514,9 +658,7 @@ class SimpleKeyboard {
|
|||||||
* Get the keyboard’s input (You can also get it from the onChange prop).
|
* Get the keyboard’s input (You can also get it from the onChange prop).
|
||||||
* @param {string} [inputName] optional - the internal input to select
|
* @param {string} [inputName] optional - the internal input to select
|
||||||
*/
|
*/
|
||||||
getInput(inputName: string, skipSync = false): string {
|
getInput(inputName = this.options.inputName, skipSync = false): string {
|
||||||
inputName = inputName || this.options.inputName;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enforce syncInstanceInputs, if set
|
* Enforce syncInstanceInputs, if set
|
||||||
*/
|
*/
|
||||||
@ -553,14 +695,17 @@ class SimpleKeyboard {
|
|||||||
* @param {string} input the input value
|
* @param {string} input the input value
|
||||||
* @param {string} inputName optional - the internal input to select
|
* @param {string} inputName optional - the internal input to select
|
||||||
*/
|
*/
|
||||||
setInput(input: string, inputName: string): void {
|
setInput(
|
||||||
inputName = inputName || this.options.inputName;
|
input: string,
|
||||||
|
inputName = this.options.inputName,
|
||||||
|
skipSync?: boolean
|
||||||
|
): void {
|
||||||
this.input[inputName] = input;
|
this.input[inputName] = input;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enforce syncInstanceInputs, if set
|
* Enforce syncInstanceInputs, if set
|
||||||
*/
|
*/
|
||||||
if (this.options.syncInstanceInputs) this.syncInstanceInputs();
|
if (!skipSync && this.options.syncInstanceInputs) this.syncInstanceInputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -587,7 +732,7 @@ class SimpleKeyboard {
|
|||||||
/**
|
/**
|
||||||
* Some option changes require adjustments before re-render
|
* Some option changes require adjustments before re-render
|
||||||
*/
|
*/
|
||||||
this.onSetOptions(options);
|
this.onSetOptions(changedOptions);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rendering
|
* Rendering
|
||||||
@ -612,8 +757,11 @@ class SimpleKeyboard {
|
|||||||
* Executing actions depending on changed options
|
* Executing actions depending on changed options
|
||||||
* @param {object} options The options to set
|
* @param {object} options The options to set
|
||||||
*/
|
*/
|
||||||
onSetOptions(options: Partial<KeyboardOptions>): void {
|
onSetOptions(changedOptions: string[] = []): void {
|
||||||
if (options.inputName) {
|
/**
|
||||||
|
* Changed: inputName
|
||||||
|
*/
|
||||||
|
if (changedOptions.includes("inputName")) {
|
||||||
/**
|
/**
|
||||||
* inputName changed. This requires a caretPosition reset
|
* inputName changed. This requires a caretPosition reset
|
||||||
*/
|
*/
|
||||||
@ -622,14 +770,47 @@ class SimpleKeyboard {
|
|||||||
}
|
}
|
||||||
this.setCaretPosition(null);
|
this.setCaretPosition(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changed: layoutName
|
||||||
|
*/
|
||||||
|
if (changedOptions.includes("layoutName")) {
|
||||||
|
/**
|
||||||
|
* Reset candidateBox
|
||||||
|
*/
|
||||||
|
if (this.candidateBox) {
|
||||||
|
this.candidateBox.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changed: layoutCandidatesPageSize, layoutCandidates
|
||||||
|
*/
|
||||||
|
if (
|
||||||
|
changedOptions.includes("layoutCandidatesPageSize") ||
|
||||||
|
changedOptions.includes("layoutCandidates")
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Reset and recreate candidateBox
|
||||||
|
*/
|
||||||
|
if (this.candidateBox) {
|
||||||
|
this.candidateBox.destroy();
|
||||||
|
this.candidateBox = new CandidateBox({
|
||||||
|
utilities: this.utilities,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all keyboard rows and reset keyboard values.
|
* Remove all keyboard rows and reset keyboard values.
|
||||||
* Used internally between re-renders.
|
* Used internally between re-renders.
|
||||||
*/
|
*/
|
||||||
clear(): void {
|
resetRows(): void {
|
||||||
this.keyboardDOM.innerHTML = "";
|
if (this.keyboardRowsDOM) {
|
||||||
|
this.keyboardRowsDOM.remove();
|
||||||
|
}
|
||||||
|
|
||||||
this.keyboardDOM.className = this.keyboardDOMClass;
|
this.keyboardDOM.className = this.keyboardDOMClass;
|
||||||
this.buttonElements = {};
|
this.buttonElements = {};
|
||||||
}
|
}
|
||||||
@ -759,7 +940,7 @@ class SimpleKeyboard {
|
|||||||
* Get the DOM Element of a button. If there are several buttons with the same name, an array of the DOM Elements is returned.
|
* 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
|
* @param {string} button The button layout name to select
|
||||||
*/
|
*/
|
||||||
getButtonElement(button: string): KeyboardButton | KeyboardButton[] {
|
getButtonElement(button: string): KeyboardElement | KeyboardElement[] {
|
||||||
let output;
|
let output;
|
||||||
|
|
||||||
const buttonArr = this.buttonElements[button];
|
const buttonArr = this.buttonElements[button];
|
||||||
@ -830,6 +1011,7 @@ class SimpleKeyboard {
|
|||||||
document.addEventListener("keydown", this.handleKeyDown);
|
document.addEventListener("keydown", this.handleKeyDown);
|
||||||
document.addEventListener("mouseup", this.handleMouseUp);
|
document.addEventListener("mouseup", this.handleMouseUp);
|
||||||
document.addEventListener("touchend", this.handleTouchEnd);
|
document.addEventListener("touchend", this.handleTouchEnd);
|
||||||
|
document.addEventListener("select", this.handleSelect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -868,6 +1050,14 @@ class SimpleKeyboard {
|
|||||||
this.caretEventHandler(event);
|
this.caretEventHandler(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event Handler: Select
|
||||||
|
*/
|
||||||
|
/* istanbul ignore next */
|
||||||
|
handleSelect(event: KeyboardHandlerEvent): void {
|
||||||
|
this.caretEventHandler(event);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by {@link setEventListeners} when an event that warrants a cursor position update is triggered
|
* Called by {@link setEventListeners} when an event that warrants a cursor position update is triggered
|
||||||
*/
|
*/
|
||||||
@ -947,6 +1137,7 @@ class SimpleKeyboard {
|
|||||||
document.removeEventListener("keydown", this.handleKeyDown);
|
document.removeEventListener("keydown", this.handleKeyDown);
|
||||||
document.removeEventListener("mouseup", this.handleMouseUp);
|
document.removeEventListener("mouseup", this.handleMouseUp);
|
||||||
document.removeEventListener("touchend", this.handleTouchEnd);
|
document.removeEventListener("touchend", this.handleTouchEnd);
|
||||||
|
document.removeEventListener("select", this.handleSelect);
|
||||||
document.onpointerup = null;
|
document.onpointerup = null;
|
||||||
document.ontouchend = null;
|
document.ontouchend = null;
|
||||||
document.ontouchcancel = null;
|
document.ontouchcancel = null;
|
||||||
@ -955,7 +1146,7 @@ class SimpleKeyboard {
|
|||||||
/**
|
/**
|
||||||
* Remove buttons
|
* Remove buttons
|
||||||
*/
|
*/
|
||||||
let deleteButton = (buttonElement: KeyboardButton) => {
|
let deleteButton = (buttonElement: KeyboardElement) => {
|
||||||
buttonElement.onpointerdown = null;
|
buttonElement.onpointerdown = null;
|
||||||
buttonElement.onpointerup = null;
|
buttonElement.onpointerup = null;
|
||||||
buttonElement.onpointercancel = null;
|
buttonElement.onpointercancel = null;
|
||||||
@ -983,9 +1174,22 @@ class SimpleKeyboard {
|
|||||||
this.keyboardDOM.onmousedown = null;
|
this.keyboardDOM.onmousedown = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clearing keyboard wrapper
|
* Clearing keyboard rows
|
||||||
*/
|
*/
|
||||||
this.clear();
|
this.resetRows();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Candidate box
|
||||||
|
*/
|
||||||
|
if (this.candidateBox) {
|
||||||
|
this.candidateBox.destroy();
|
||||||
|
this.candidateBox = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clearing keyboardDOM
|
||||||
|
*/
|
||||||
|
this.keyboardDOM.innerHTML = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove instance
|
* Remove instance
|
||||||
@ -1117,7 +1321,7 @@ class SimpleKeyboard {
|
|||||||
*/
|
*/
|
||||||
this.setEventListeners();
|
this.setEventListeners();
|
||||||
|
|
||||||
if (typeof this.options.onInit === "function") this.options.onInit();
|
if (typeof this.options.onInit === "function") this.options.onInit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1172,7 +1376,8 @@ class SimpleKeyboard {
|
|||||||
* Executes the callback function every time simple-keyboard is rendered (e.g: when you change layouts).
|
* Executes the callback function every time simple-keyboard is rendered (e.g: when you change layouts).
|
||||||
*/
|
*/
|
||||||
onRender() {
|
onRender() {
|
||||||
if (typeof this.options.onRender === "function") this.options.onRender();
|
if (typeof this.options.onRender === "function")
|
||||||
|
this.options.onRender(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1198,8 +1403,8 @@ class SimpleKeyboard {
|
|||||||
loadModules() {
|
loadModules() {
|
||||||
if (Array.isArray(this.options.modules)) {
|
if (Array.isArray(this.options.modules)) {
|
||||||
this.options.modules.forEach((KeyboardModule) => {
|
this.options.modules.forEach((KeyboardModule) => {
|
||||||
const keyboardModule = new KeyboardModule();
|
const keyboardModule = new KeyboardModule(this);
|
||||||
keyboardModule.init(this);
|
keyboardModule.init && keyboardModule.init(this);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.keyboardPluginClasses = "modules-loaded";
|
this.keyboardPluginClasses = "modules-loaded";
|
||||||
@ -1329,7 +1534,7 @@ class SimpleKeyboard {
|
|||||||
/**
|
/**
|
||||||
* Clear keyboard
|
* Clear keyboard
|
||||||
*/
|
*/
|
||||||
this.clear();
|
this.resetRows();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calling beforeFirstRender
|
* Calling beforeFirstRender
|
||||||
@ -1360,11 +1565,29 @@ class SimpleKeyboard {
|
|||||||
useTouchEventsClass
|
useTouchEventsClass
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create row wrapper
|
||||||
|
*/
|
||||||
|
this.keyboardRowsDOM = document.createElement("div");
|
||||||
|
this.keyboardRowsDOM.className = "hg-rows";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterating through each row
|
* Iterating through each row
|
||||||
*/
|
*/
|
||||||
layout[this.options.layoutName].forEach((row, rIndex) => {
|
layout[this.options.layoutName].forEach((row, rIndex) => {
|
||||||
const rowArray = row.split(" ");
|
let rowArray = row.split(" ");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enforce excludeFromLayout
|
||||||
|
*/
|
||||||
|
if (this.options.excludeFromLayout[this.options.layoutName]) {
|
||||||
|
rowArray = rowArray.filter(
|
||||||
|
(buttonName) =>
|
||||||
|
!this.options.excludeFromLayout[this.options.layoutName].includes(
|
||||||
|
buttonName
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creating empty row
|
* Creating empty row
|
||||||
@ -1465,7 +1688,7 @@ class SimpleKeyboard {
|
|||||||
* Handle PointerEvents
|
* Handle PointerEvents
|
||||||
*/
|
*/
|
||||||
buttonDOM.onpointerdown = (e: KeyboardHandlerEvent) => {
|
buttonDOM.onpointerdown = (e: KeyboardHandlerEvent) => {
|
||||||
this.handleButtonClicked(button);
|
this.handleButtonClicked(button, e);
|
||||||
this.handleButtonMouseDown(button, e);
|
this.handleButtonMouseDown(button, e);
|
||||||
};
|
};
|
||||||
buttonDOM.onpointerup = (e: KeyboardHandlerEvent) => {
|
buttonDOM.onpointerup = (e: KeyboardHandlerEvent) => {
|
||||||
@ -1483,7 +1706,7 @@ class SimpleKeyboard {
|
|||||||
* Handle touch events
|
* Handle touch events
|
||||||
*/
|
*/
|
||||||
buttonDOM.ontouchstart = (e: KeyboardHandlerEvent) => {
|
buttonDOM.ontouchstart = (e: KeyboardHandlerEvent) => {
|
||||||
this.handleButtonClicked(button);
|
this.handleButtonClicked(button, e);
|
||||||
this.handleButtonMouseDown(button, e);
|
this.handleButtonMouseDown(button, e);
|
||||||
};
|
};
|
||||||
buttonDOM.ontouchend = (e: KeyboardHandlerEvent) => {
|
buttonDOM.ontouchend = (e: KeyboardHandlerEvent) => {
|
||||||
@ -1496,9 +1719,9 @@ class SimpleKeyboard {
|
|||||||
/**
|
/**
|
||||||
* Handle mouse events
|
* Handle mouse events
|
||||||
*/
|
*/
|
||||||
buttonDOM.onclick = () => {
|
buttonDOM.onclick = (e: KeyboardHandlerEvent) => {
|
||||||
this.isMouseHold = false;
|
this.isMouseHold = false;
|
||||||
this.handleButtonClicked(button);
|
this.handleButtonClicked(button, e);
|
||||||
};
|
};
|
||||||
buttonDOM.onmousedown = (e: KeyboardHandlerEvent) => {
|
buttonDOM.onmousedown = (e: KeyboardHandlerEvent) => {
|
||||||
this.handleButtonMouseDown(button, e);
|
this.handleButtonMouseDown(button, e);
|
||||||
@ -1551,11 +1774,16 @@ class SimpleKeyboard {
|
|||||||
containerEndIndexes
|
containerEndIndexes
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appending row to hg-rows
|
||||||
|
*/
|
||||||
|
this.keyboardRowsDOM.appendChild(rowDOM);
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appending row to keyboard
|
* Appending row to keyboard
|
||||||
*/
|
*/
|
||||||
this.keyboardDOM.appendChild(rowDOM);
|
this.keyboardDOM.appendChild(this.keyboardRowsDOM);
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calling onRender
|
* Calling onRender
|
||||||
@ -1577,15 +1805,18 @@ class SimpleKeyboard {
|
|||||||
!useTouchEvents &&
|
!useTouchEvents &&
|
||||||
!useMouseEvents
|
!useMouseEvents
|
||||||
) {
|
) {
|
||||||
document.onpointerup = () => this.handleButtonMouseUp();
|
document.onpointerup = (e: KeyboardHandlerEvent) =>
|
||||||
|
this.handleButtonMouseUp(null, e);
|
||||||
this.keyboardDOM.onpointerdown = (e: KeyboardHandlerEvent) =>
|
this.keyboardDOM.onpointerdown = (e: KeyboardHandlerEvent) =>
|
||||||
this.handleKeyboardContainerMouseDown(e);
|
this.handleKeyboardContainerMouseDown(e);
|
||||||
} else if (useTouchEvents) {
|
} else if (useTouchEvents) {
|
||||||
/**
|
/**
|
||||||
* Handling ontouchend, ontouchcancel
|
* Handling ontouchend, ontouchcancel
|
||||||
*/
|
*/
|
||||||
document.ontouchend = () => this.handleButtonMouseUp();
|
document.ontouchend = (e: KeyboardHandlerEvent) =>
|
||||||
document.ontouchcancel = () => this.handleButtonMouseUp();
|
this.handleButtonMouseUp(null, e);
|
||||||
|
document.ontouchcancel = (e: KeyboardHandlerEvent) =>
|
||||||
|
this.handleButtonMouseUp(null, e);
|
||||||
|
|
||||||
this.keyboardDOM.ontouchstart = (e: KeyboardHandlerEvent) =>
|
this.keyboardDOM.ontouchstart = (e: KeyboardHandlerEvent) =>
|
||||||
this.handleKeyboardContainerMouseDown(e);
|
this.handleKeyboardContainerMouseDown(e);
|
||||||
@ -1593,7 +1824,8 @@ class SimpleKeyboard {
|
|||||||
/**
|
/**
|
||||||
* Handling mouseup
|
* Handling mouseup
|
||||||
*/
|
*/
|
||||||
document.onmouseup = () => this.handleButtonMouseUp();
|
document.onmouseup = (e: KeyboardHandlerEvent) =>
|
||||||
|
this.handleButtonMouseUp(null, e);
|
||||||
this.keyboardDOM.onmousedown = (e: KeyboardHandlerEvent) =>
|
this.keyboardDOM.onmousedown = (e: KeyboardHandlerEvent) =>
|
||||||
this.handleKeyboardContainerMouseDown(e);
|
this.handleKeyboardContainerMouseDown(e);
|
||||||
}
|
}
|
||||||
|
68
src/lib/components/css/CandidateBox.css
Normal file
68
src/lib/components/css/CandidateBox.css
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
.hg-candidate-box {
|
||||||
|
display: inline-flex;
|
||||||
|
border-radius: 5px;
|
||||||
|
position: absolute;
|
||||||
|
background: #ececec;
|
||||||
|
border-bottom: 2px solid #b5b5b5;
|
||||||
|
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, 0.03);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.hg-candidate-box-list-item:active {
|
||||||
|
background: rgba(0, 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;
|
||||||
|
}
|
371
src/lib/components/tests/CandidateBox.test.js
Normal file
371
src/lib/components/tests/CandidateBox.test.js
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
import Keyboard from '../Keyboard';
|
||||||
|
import { setDOM, clearDOM } from '../../../utils/TestUtility';
|
||||||
|
import CandidateBox from '../CandidateBox';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
setDOM();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
clearDOM();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox class will be instantiated by default', () => {
|
||||||
|
document.querySelector("body").innerHTML = `
|
||||||
|
<input class="input" placeholder="Tap on the virtual keyboard to start" />
|
||||||
|
<div class="simple-keyboard"></div>
|
||||||
|
|
||||||
|
<input class="input2" placeholder="Tap on the virtual keyboard to start" />
|
||||||
|
<div class="keyboard2"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const keyboard1 = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const keyboard2 = new Keyboard(".keyboard2", {
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(keyboard1.candidateBox).toBeInstanceOf(CandidateBox);
|
||||||
|
expect(keyboard2.candidateBox).toBeInstanceOf(CandidateBox);
|
||||||
|
|
||||||
|
keyboard1.destroy();
|
||||||
|
keyboard2.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox class will not be instantiated on enableLayoutCandidates: false', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2"
|
||||||
|
},
|
||||||
|
enableLayoutCandidates: false
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox).toBeNull();
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox will respect layoutCandidatesPageSize', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2 3 4 5 6"
|
||||||
|
},
|
||||||
|
layoutCandidatesPageSize: 3
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click();
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelectorAll("li").length).toBe(3);
|
||||||
|
|
||||||
|
keyboard.getButtonElement("{bksp}").click();
|
||||||
|
keyboard.setOptions({
|
||||||
|
layoutCandidatesPageSize: 6
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click();
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelectorAll("li").length).toBe(6);
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox will respect layoutCandidatesPageSize', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2 3 4 5 6"
|
||||||
|
},
|
||||||
|
layoutCandidatesPageSize: 3
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click();
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelectorAll("li").length).toBe(3);
|
||||||
|
|
||||||
|
keyboard.getButtonElement("{bksp}").click();
|
||||||
|
keyboard.setOptions({
|
||||||
|
layoutCandidatesPageSize: 6
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click();
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelectorAll("li").length).toBe(6);
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox will reset on layoutCandidates change', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2 3 4 5 6"
|
||||||
|
},
|
||||||
|
layoutCandidatesPageSize: 3
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click();
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelector("li").textContent).toBe("1");
|
||||||
|
|
||||||
|
keyboard.getButtonElement("{bksp}").click();
|
||||||
|
keyboard.setOptions({
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "s1 s2 s3 s4 s5 s6"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click();
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelector("li").textContent).toBe("s1");
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox show will return early if candidateValue is not provided', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.candidateBox.renderPage = jest.fn();
|
||||||
|
keyboard.candidateBox.show({
|
||||||
|
candidateValue: null,
|
||||||
|
targetElement: document.createElement("div"),
|
||||||
|
onSelect: () => {}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox.renderPage).not.toBeCalled();
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox show will call renderPage if candidateValue is provided', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.candidateBox.renderPage = jest.fn();
|
||||||
|
keyboard.candidateBox.show({
|
||||||
|
candidateValue: "a b",
|
||||||
|
targetElement: document.createElement("div"),
|
||||||
|
onSelect: () => {}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox.renderPage).toBeCalled();
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox select candidate will work', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2 3 4 5 6"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let candidateBoxOnItemSelected;
|
||||||
|
const onSelect = jest.fn().mockImplementation((selectedCandidate) => {
|
||||||
|
candidateBoxOnItemSelected(selectedCandidate);
|
||||||
|
keyboard.candidateBox.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const candidateBoxRenderFn = keyboard.candidateBox.renderPage;
|
||||||
|
jest.spyOn(keyboard.candidateBox, "renderPage").mockImplementation((params) => {
|
||||||
|
candidateBoxOnItemSelected = params.onItemSelected;
|
||||||
|
params.onItemSelected = onSelect;
|
||||||
|
candidateBoxRenderFn(params);
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click();
|
||||||
|
keyboard.candidateBox.candidateBoxElement.querySelector("li").click();
|
||||||
|
|
||||||
|
expect(onSelect).toBeCalledWith("1");
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox select next and previous page will work', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2 3 4 5 6"
|
||||||
|
},
|
||||||
|
layoutCandidatesPageSize: 3
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click();
|
||||||
|
keyboard.candidateBox.candidateBoxElement.querySelector(".hg-candidate-box-next").click();
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelector("ul").textContent).toBe("456");
|
||||||
|
|
||||||
|
keyboard.candidateBox.candidateBoxElement.querySelector(".hg-candidate-box-prev").click();
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelector("ul").textContent).toBe("123");
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox selecting next and previous page when not available will do nothing', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2 3 4 5 6"
|
||||||
|
},
|
||||||
|
layoutCandidatesPageSize: 3
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click();
|
||||||
|
|
||||||
|
keyboard.candidateBox.candidateBoxElement.querySelector(".hg-candidate-box-prev").click();
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelector("ul").textContent).toBe("123");
|
||||||
|
|
||||||
|
keyboard.candidateBox.candidateBoxElement.querySelector(".hg-candidate-box-next").click();
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelector("ul").textContent).toBe("456");
|
||||||
|
|
||||||
|
keyboard.candidateBox.candidateBoxElement.querySelector(".hg-candidate-box-next").click();
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelector("ul").textContent).toBe("456");
|
||||||
|
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox will not show anything when there is no candidates', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2 3 4 5 6"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("b").click();
|
||||||
|
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement).toBeUndefined();
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox will propose the better matching result, regardless of layoutCandidates order', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2 3",
|
||||||
|
aa: "6 7 8"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click();
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelector("ul").textContent).toBe("123");
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click(); // This will get you the 'aa' layoutCandidates instead of the 'a' ones.
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelector("ul").textContent).toBe("678");
|
||||||
|
|
||||||
|
keyboard.getButtonElement("{bksp}").click();
|
||||||
|
expect(keyboard.candidateBox.candidateBoxElement.querySelector("ul").textContent).toBe("123");
|
||||||
|
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox show not be called if keyboard.candidateBox is undefined upon showCandidatesBox call', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2 3",
|
||||||
|
aa: "6 7 8"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.candidateBox = null;
|
||||||
|
keyboard.showCandidatesBox();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('CandidateBox selection should trigger onChange', () => {
|
||||||
|
const keyboard = new Keyboard({
|
||||||
|
layout: {
|
||||||
|
default: [
|
||||||
|
"a b {bksp}"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
layoutCandidates: {
|
||||||
|
a: "1 2 3 4 5 6"
|
||||||
|
},
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onChangeAll: jest.fn()
|
||||||
|
});
|
||||||
|
|
||||||
|
let candidateBoxOnItemSelected;
|
||||||
|
|
||||||
|
const onSelect = jest.fn().mockImplementation((selectedCandidate) => {
|
||||||
|
candidateBoxOnItemSelected(selectedCandidate);
|
||||||
|
keyboard.candidateBox.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const candidateBoxRenderFn = keyboard.candidateBox.renderPage;
|
||||||
|
jest.spyOn(keyboard.candidateBox, "renderPage").mockImplementation((params) => {
|
||||||
|
candidateBoxOnItemSelected = params.onItemSelected;
|
||||||
|
params.onItemSelected = onSelect;
|
||||||
|
candidateBoxRenderFn(params);
|
||||||
|
});
|
||||||
|
|
||||||
|
keyboard.getButtonElement("a").click();
|
||||||
|
keyboard.candidateBox.candidateBoxElement.querySelector("li").click();
|
||||||
|
|
||||||
|
expect(keyboard.options.onChange).toBeCalledWith("a");
|
||||||
|
expect(keyboard.options.onChangeAll).toBeCalledWith({"default": "a"});
|
||||||
|
|
||||||
|
expect(keyboard.options.onChange).toBeCalledWith("1");
|
||||||
|
expect(keyboard.options.onChangeAll).toBeCalledWith({"default": "1"});
|
||||||
|
keyboard.destroy();
|
||||||
|
});
|
@ -1,2 +1,3 @@
|
|||||||
|
import "./polyfills";
|
||||||
import SimpleKeyboard from "./components/Keyboard";
|
import SimpleKeyboard from "./components/Keyboard";
|
||||||
export default SimpleKeyboard;
|
export default SimpleKeyboard;
|
||||||
|
252
src/lib/polyfills.js
Normal file
252
src/lib/polyfills.js
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
// element.remove() polyfill
|
||||||
|
if (typeof Element !== "undefined" && !("remove" in Element.prototype)) {
|
||||||
|
Element.prototype["remove"] = function () {
|
||||||
|
if (this.parentNode) {
|
||||||
|
this.parentNode.removeChild(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* classList.js: Cross-browser full element.classList implementation.
|
||||||
|
* 1.2.20171210
|
||||||
|
*
|
||||||
|
* By Eli Grey, http://eligrey.com
|
||||||
|
* License: Dedicated to the public domain.
|
||||||
|
* See https://github.com/eligrey/classList.js/blob/master/LICENSE.md
|
||||||
|
*/
|
||||||
|
if (typeof self !== "undefined" && "document" in self) {
|
||||||
|
// Full polyfill for browsers with no classList support
|
||||||
|
// Including IE < Edge missing SVGElement.classList
|
||||||
|
if (
|
||||||
|
!("classList" in document.createElement("_")) ||
|
||||||
|
(document.createElementNS &&
|
||||||
|
!(
|
||||||
|
"classList" in
|
||||||
|
document.createElementNS("http://www.w3.org/2000/svg", "g")
|
||||||
|
))
|
||||||
|
) {
|
||||||
|
(function (view) {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
if (!("Element" in view)) return;
|
||||||
|
|
||||||
|
var classListProp = "classList",
|
||||||
|
protoProp = "prototype",
|
||||||
|
elemCtrProto = view.Element[protoProp],
|
||||||
|
objCtr = Object,
|
||||||
|
strTrim =
|
||||||
|
String[protoProp].trim ||
|
||||||
|
function () {
|
||||||
|
return this.replace(/^\s+|\s+$/g, "");
|
||||||
|
},
|
||||||
|
arrIndexOf =
|
||||||
|
Array[protoProp].indexOf ||
|
||||||
|
function (item) {
|
||||||
|
var i = 0,
|
||||||
|
len = this.length;
|
||||||
|
for (; i < len; i++) {
|
||||||
|
if (i in this && this[i] === item) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
},
|
||||||
|
// Vendors: please allow content code to instantiate DOMExceptions
|
||||||
|
DOMEx = function (type, message) {
|
||||||
|
this.name = type;
|
||||||
|
this.code = DOMException[type];
|
||||||
|
this.message = message;
|
||||||
|
},
|
||||||
|
checkTokenAndGetIndex = function (classList, token) {
|
||||||
|
if (token === "") {
|
||||||
|
throw new DOMEx("SYNTAX_ERR", "The token must not be empty.");
|
||||||
|
}
|
||||||
|
if (/\s/.test(token)) {
|
||||||
|
throw new DOMEx(
|
||||||
|
"INVALID_CHARACTER_ERR",
|
||||||
|
"The token must not contain space characters."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return arrIndexOf.call(classList, token);
|
||||||
|
},
|
||||||
|
ClassList = function (elem) {
|
||||||
|
var trimmedClasses = strTrim.call(elem.getAttribute("class") || ""),
|
||||||
|
classes = trimmedClasses ? trimmedClasses.split(/\s+/) : [],
|
||||||
|
i = 0,
|
||||||
|
len = classes.length;
|
||||||
|
for (; i < len; i++) {
|
||||||
|
this.push(classes[i]);
|
||||||
|
}
|
||||||
|
this._updateClassName = function () {
|
||||||
|
elem.setAttribute("class", this.toString());
|
||||||
|
};
|
||||||
|
},
|
||||||
|
classListProto = (ClassList[protoProp] = []),
|
||||||
|
classListGetter = function () {
|
||||||
|
return new ClassList(this);
|
||||||
|
};
|
||||||
|
// Most DOMException implementations don't allow calling DOMException's toString()
|
||||||
|
// on non-DOMExceptions. Error's toString() is sufficient here.
|
||||||
|
DOMEx[protoProp] = Error[protoProp];
|
||||||
|
classListProto.item = function (i) {
|
||||||
|
return this[i] || null;
|
||||||
|
};
|
||||||
|
classListProto.contains = function (token) {
|
||||||
|
return ~checkTokenAndGetIndex(this, token + "");
|
||||||
|
};
|
||||||
|
classListProto.add = function () {
|
||||||
|
var tokens = arguments,
|
||||||
|
i = 0,
|
||||||
|
l = tokens.length,
|
||||||
|
token,
|
||||||
|
updated = false;
|
||||||
|
do {
|
||||||
|
token = tokens[i] + "";
|
||||||
|
if (!~checkTokenAndGetIndex(this, token)) {
|
||||||
|
this.push(token);
|
||||||
|
updated = true;
|
||||||
|
}
|
||||||
|
} while (++i < l);
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
this._updateClassName();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
classListProto.remove = function () {
|
||||||
|
var tokens = arguments,
|
||||||
|
i = 0,
|
||||||
|
l = tokens.length,
|
||||||
|
token,
|
||||||
|
updated = false,
|
||||||
|
index;
|
||||||
|
do {
|
||||||
|
token = tokens[i] + "";
|
||||||
|
index = checkTokenAndGetIndex(this, token);
|
||||||
|
while (~index) {
|
||||||
|
this.splice(index, 1);
|
||||||
|
updated = true;
|
||||||
|
index = checkTokenAndGetIndex(this, token);
|
||||||
|
}
|
||||||
|
} while (++i < l);
|
||||||
|
|
||||||
|
if (updated) {
|
||||||
|
this._updateClassName();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
classListProto.toggle = function (token, force) {
|
||||||
|
var result = this.contains(token),
|
||||||
|
method = result
|
||||||
|
? force !== true && "remove"
|
||||||
|
: force !== false && "add";
|
||||||
|
if (method) {
|
||||||
|
this[method](token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (force === true || force === false) {
|
||||||
|
return force;
|
||||||
|
} else {
|
||||||
|
return !result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
classListProto.replace = function (token, replacement_token) {
|
||||||
|
var index = checkTokenAndGetIndex(token + "");
|
||||||
|
if (~index) {
|
||||||
|
this.splice(index, 1, replacement_token);
|
||||||
|
this._updateClassName();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
classListProto.toString = function () {
|
||||||
|
return this.join(" ");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (objCtr.defineProperty) {
|
||||||
|
var classListPropDesc = {
|
||||||
|
get: classListGetter,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc);
|
||||||
|
} catch (ex) {
|
||||||
|
// IE 8 doesn't support enumerable:true
|
||||||
|
// adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36
|
||||||
|
// modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected
|
||||||
|
if (ex.number === undefined || ex.number === -0x7ff5ec54) {
|
||||||
|
classListPropDesc.enumerable = false;
|
||||||
|
objCtr.defineProperty(
|
||||||
|
elemCtrProto,
|
||||||
|
classListProp,
|
||||||
|
classListPropDesc
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (objCtr[protoProp].__defineGetter__) {
|
||||||
|
elemCtrProto.__defineGetter__(classListProp, classListGetter);
|
||||||
|
}
|
||||||
|
})(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// There is full or partial native classList support, so just check if we need
|
||||||
|
// to normalize the add/remove and toggle APIs.
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var testElement = document.createElement("_");
|
||||||
|
|
||||||
|
testElement.classList.add("c1", "c2");
|
||||||
|
|
||||||
|
// Polyfill for IE 10/11 and Firefox <26, where classList.add and
|
||||||
|
// classList.remove exist but support only one argument at a time.
|
||||||
|
if (!testElement.classList.contains("c2")) {
|
||||||
|
var createMethod = function (method) {
|
||||||
|
var original = DOMTokenList.prototype[method];
|
||||||
|
|
||||||
|
DOMTokenList.prototype[method] = function (token) {
|
||||||
|
var i,
|
||||||
|
len = arguments.length;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
token = arguments[i];
|
||||||
|
original.call(this, token);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
createMethod("add");
|
||||||
|
createMethod("remove");
|
||||||
|
}
|
||||||
|
|
||||||
|
testElement.classList.toggle("c3", false);
|
||||||
|
|
||||||
|
// Polyfill for IE 10 and Firefox <24, where classList.toggle does not
|
||||||
|
// support the second argument.
|
||||||
|
if (testElement.classList.contains("c3")) {
|
||||||
|
var _toggle = DOMTokenList.prototype.toggle;
|
||||||
|
|
||||||
|
DOMTokenList.prototype.toggle = function (token, force) {
|
||||||
|
if (1 in arguments && !this.contains(token) === !force) {
|
||||||
|
return force;
|
||||||
|
} else {
|
||||||
|
return _toggle.call(this, token);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace() polyfill
|
||||||
|
if (!("replace" in document.createElement("_").classList)) {
|
||||||
|
DOMTokenList.prototype.replace = function (token, replacement_token) {
|
||||||
|
var tokens = this.toString().split(" "),
|
||||||
|
index = tokens.indexOf(token + "");
|
||||||
|
if (~index) {
|
||||||
|
tokens = tokens.slice(index);
|
||||||
|
this.remove.apply(this, tokens);
|
||||||
|
this.add(replacement_token);
|
||||||
|
this.add.apply(this, tokens.slice(1));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
testElement = null;
|
||||||
|
})();
|
||||||
|
}
|
@ -26,26 +26,23 @@ const banner = `
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
mode: "production",
|
mode: "production",
|
||||||
devtool: 'source-map',
|
|
||||||
entry: './src/lib/index.ts',
|
entry: './src/lib/index.ts',
|
||||||
output: {
|
output: {
|
||||||
filename: 'index.js',
|
filename: 'index.js',
|
||||||
path: path.resolve(__dirname, 'build'),
|
path: path.resolve(__dirname, 'build'),
|
||||||
library: "SimpleKeyboard",
|
library: "SimpleKeyboard",
|
||||||
libraryTarget: 'umd',
|
libraryTarget: 'umd',
|
||||||
clean: true
|
clean: true,
|
||||||
|
globalObject: 'this',
|
||||||
|
environment: {
|
||||||
|
arrowFunction: false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize: true,
|
minimize: true,
|
||||||
minimizer: [
|
minimizer: [
|
||||||
new TerserPlugin({ extractComments: false }),
|
new TerserPlugin({ extractComments: false }),
|
||||||
new OptimizeCSSAssetsPlugin({
|
new OptimizeCSSAssetsPlugin()
|
||||||
cssProcessorOptions: {
|
|
||||||
map: {
|
|
||||||
inline: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
devServer: {
|
devServer: {
|
||||||
@ -58,7 +55,7 @@ module.exports = {
|
|||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
test: /\.(m|j|t)s$/,
|
test: /\.m?(j|t)s$/,
|
||||||
exclude: /(node_modules|bower_components)/,
|
exclude: /(node_modules|bower_components)/,
|
||||||
use: {
|
use: {
|
||||||
loader: 'babel-loader'
|
loader: 'babel-loader'
|
||||||
@ -68,7 +65,19 @@ module.exports = {
|
|||||||
test: /\.(sa|sc|c)ss$/,
|
test: /\.(sa|sc|c)ss$/,
|
||||||
use: [
|
use: [
|
||||||
MiniCssExtractPlugin.loader,
|
MiniCssExtractPlugin.loader,
|
||||||
{ loader: "css-loader", options: { sourceMap: true } },
|
{ loader: "css-loader" },
|
||||||
|
{
|
||||||
|
loader: "postcss-loader",
|
||||||
|
options: {
|
||||||
|
postcssOptions: {
|
||||||
|
plugins: [
|
||||||
|
[
|
||||||
|
"autoprefixer"
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user