mirror of
https://github.com/hodgef/simple-keyboard.git
synced 2025-03-03 16:02:56 +08:00
Port to typescript
This commit is contained in:
parent
60d8d43b3c
commit
d1c414b7b5
@ -1,30 +1,27 @@
|
||||
{
|
||||
"parser": "babel-eslint",
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json",
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true,
|
||||
"modules": true,
|
||||
"experimentalObjectRestSpread": true
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
"react"
|
||||
],
|
||||
"extends": ["eslint:recommended", "plugin:react/recommended"],
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
"rules": {
|
||||
"comma-dangle": 0,
|
||||
"no-unused-vars": "warn",
|
||||
"no-unexpected-multiline": "warn",
|
||||
"prefer-const": "warn"
|
||||
},
|
||||
"settings": {
|
||||
"react": {
|
||||
"pragma": "React",
|
||||
"version": "15.6.1"
|
||||
}
|
||||
"prefer-const": "warn",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-var-requires": "off"
|
||||
},
|
||||
"settings": {},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
@ -32,4 +29,4 @@
|
||||
"jest": true,
|
||||
"es6": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
63
.github/workflows/main.yml
vendored
63
.github/workflows/main.yml
vendored
@ -9,36 +9,33 @@ jobs:
|
||||
node-version: [12.x]
|
||||
os: [ubuntu-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
- name: npm install, build, and test
|
||||
run: |
|
||||
npm install
|
||||
npm run start -- --testMode
|
||||
npm run demo
|
||||
npm run coverage
|
||||
- uses: codecov/codecov-action@v1.0.2
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: Discord notification
|
||||
if: success()
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@master
|
||||
with:
|
||||
args: 'simple-keyboard - CI Build Passed'
|
||||
|
||||
- name: Discord notification
|
||||
if: failure()
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@master
|
||||
with:
|
||||
args: 'simple-keyboard - CI Build Failed'
|
||||
|
||||
- uses: actions/checkout@v1
|
||||
- name: Use Node.js ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
- name: npm install, build, and test
|
||||
run: |
|
||||
npm install
|
||||
npm run coverage
|
||||
- uses: codecov/codecov-action@v1.0.2
|
||||
with:
|
||||
token: ${{secrets.CODECOV_TOKEN}}
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: Discord notification
|
||||
if: success()
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@master
|
||||
with:
|
||||
args: "simple-keyboard - CI Build Passed"
|
||||
|
||||
- name: Discord notification
|
||||
if: failure()
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@master
|
||||
with:
|
||||
args: "simple-keyboard - CI Build Failed"
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -5,9 +5,6 @@
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# production
|
||||
/demo
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
|
10
.npmignore
10
.npmignore
@ -2,6 +2,7 @@
|
||||
/node_modules
|
||||
|
||||
# testing
|
||||
/tests
|
||||
/coverage
|
||||
|
||||
# docs
|
||||
@ -13,12 +14,13 @@
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
/.github
|
||||
/demo
|
||||
.esdoc.json
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
/.github
|
||||
/demo
|
||||
|
||||
# Development folders and files
|
||||
public
|
||||
@ -29,4 +31,6 @@ config
|
||||
CHANGELOG.md
|
||||
README.md
|
||||
.eslintignore
|
||||
.eslintrc.json
|
||||
.eslintrc.json
|
||||
webpack.config.js
|
||||
babel.config.js
|
19
.travis.yml
19
.travis.yml
@ -1,19 +0,0 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- node
|
||||
install:
|
||||
- npm install -g codecov
|
||||
- npm install
|
||||
script:
|
||||
- npm run start -- --testMode
|
||||
- npm run demo
|
||||
- npm run coverage
|
||||
- codecov
|
||||
after_success:
|
||||
- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
|
||||
- chmod +x send.sh
|
||||
- ./send.sh success $WEBHOOK_URL
|
||||
after_failure:
|
||||
- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
|
||||
- chmod +x send.sh
|
||||
- ./send.sh failure $WEBHOOK_URL
|
2
LICENSE
2
LICENSE
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
||||
|
9
babel.config.js
Normal file
9
babel.config.js
Normal file
@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
["@babel/env"]
|
||||
],
|
||||
plugins: [
|
||||
["@babel/plugin-proposal-class-properties"],
|
||||
["@babel/plugin-transform-typescript"]
|
||||
]
|
||||
};
|
@ -1,12 +1,11 @@
|
||||
/*!
|
||||
*
|
||||
* simple-keyboard v2.32.119
|
||||
* simple-keyboard v3.0.0-beta.0
|
||||
* https://github.com/hodgef/simple-keyboard
|
||||
*
|
||||
* Copyright (c) Francisco Hodge (https://github.com/hodgef)
|
||||
*
|
||||
*
|
||||
* Copyright (c) Francisco Hodge (https://github.com/hodgef) and project contributors.
|
||||
*
|
||||
* 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%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;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}
|
||||
/*# sourceMappingURL=index.css.map */
|
||||
*
|
||||
*/.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}
|
File diff suppressed because one or more lines are too long
945
build/index.d.ts
vendored
945
build/index.d.ts
vendored
@ -1,329 +1,618 @@
|
||||
declare module 'simple-keyboard' {
|
||||
export interface KeyboardLayoutObject {
|
||||
[key: string]: string[];
|
||||
}
|
||||
|
||||
export interface KeyboardButtonTheme {
|
||||
class: string;
|
||||
buttons: string;
|
||||
}
|
||||
|
||||
export interface KeyboardButtonAttributes {
|
||||
attribute: string;
|
||||
value: string;
|
||||
buttons: string;
|
||||
}
|
||||
|
||||
export interface KeyboardOptions {
|
||||
/**
|
||||
* Modify the keyboard layout.
|
||||
*/
|
||||
layout?: KeyboardLayoutObject;
|
||||
|
||||
/**
|
||||
* Specifies which layout should be used.
|
||||
*/
|
||||
layoutName?: string;
|
||||
|
||||
/**
|
||||
* Replaces variable buttons (such as `{bksp}`) with a human-friendly name (e.g.: `backspace`).
|
||||
*/
|
||||
display?: { [button: string]: string };
|
||||
|
||||
/**
|
||||
* By default, when you set the display property, you replace the default one. This setting merges them instead.
|
||||
*/
|
||||
mergeDisplay?: boolean;
|
||||
|
||||
/**
|
||||
* A prop to add your own css classes to the keyboard wrapper. You can add multiple classes separated by a space.
|
||||
*/
|
||||
theme?: string;
|
||||
|
||||
/**
|
||||
* A prop to add your own css classes to one or several buttons.
|
||||
*/
|
||||
buttonTheme?: KeyboardButtonTheme[];
|
||||
|
||||
/**
|
||||
* A prop to add your own attributes to one or several buttons.
|
||||
*/
|
||||
buttonAttributes?: KeyboardButtonAttributes[];
|
||||
|
||||
/**
|
||||
* Runs a `console.log` every time a key is pressed. Displays the buttons pressed and the current input.
|
||||
*/
|
||||
debug?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies whether clicking the "ENTER" button will input a newline (`\n`) or not.
|
||||
*/
|
||||
newLineOnEnter?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies whether clicking the "TAB" button will input a tab character (`\t`) or not.
|
||||
*/
|
||||
tabCharOnTab?: boolean;
|
||||
|
||||
/**
|
||||
* Allows you to use a single simple-keyboard instance for several inputs.
|
||||
*/
|
||||
inputName?: string;
|
||||
|
||||
/**
|
||||
* `number`: Restrains all of simple-keyboard inputs to a certain length. This should be used in addition to the input element’s maxlengthattribute.
|
||||
*
|
||||
* `{ [inputName: string]: number }`: Restrains simple-keyboard’s individual inputs to a certain length. This should be used in addition to the input element’s maxlengthattribute.
|
||||
*/
|
||||
maxLength?: any;
|
||||
|
||||
/**
|
||||
* When set to true, this option synchronizes the internal input of every simple-keyboard instance.
|
||||
*/
|
||||
syncInstanceInputs?: boolean;
|
||||
|
||||
/**
|
||||
* Enable highlighting of keys pressed on physical keyboard.
|
||||
*/
|
||||
physicalKeyboardHighlight?: boolean;
|
||||
|
||||
/**
|
||||
* Presses keys highlighted by physicalKeyboardHighlight
|
||||
*/
|
||||
physicalKeyboardHighlightPress?: boolean;
|
||||
|
||||
/**
|
||||
* Define the text color that the physical keyboard highlighted key should have.
|
||||
*/
|
||||
physicalKeyboardHighlightTextColor?: string;
|
||||
|
||||
/**
|
||||
* Define the background color that the physical keyboard highlighted key should have.
|
||||
*/
|
||||
physicalKeyboardHighlightBgColor?: string;
|
||||
|
||||
/**
|
||||
* Calling preventDefault for the mousedown events keeps the focus on the input.
|
||||
*/
|
||||
preventMouseDownDefault?: boolean;
|
||||
|
||||
/**
|
||||
* Calling preventDefault for the mouseup events.
|
||||
*/
|
||||
preventMouseUpDefault?: boolean;
|
||||
|
||||
/**
|
||||
* Stops pointer down events on simple-keyboard buttons from bubbling to parent elements.
|
||||
*/
|
||||
stopMouseDownPropagation?: boolean;
|
||||
|
||||
/**
|
||||
* Stops pointer up events on simple-keyboard buttons from bubbling to parent elements.
|
||||
*/
|
||||
stopMouseUpPropagation?: boolean;
|
||||
|
||||
/**
|
||||
* Render buttons as a button element instead of a div element.
|
||||
*/
|
||||
useButtonTag?: boolean;
|
||||
|
||||
/**
|
||||
* A prop to ensure characters are always be added/removed at the end of the string.
|
||||
*/
|
||||
disableCaretPositioning?: boolean;
|
||||
|
||||
/**
|
||||
* Restrains input(s) change to the defined regular expression pattern.
|
||||
*/
|
||||
inputPattern?: any;
|
||||
|
||||
/**
|
||||
* Instructs simple-keyboard to use touch events instead of click events.
|
||||
*/
|
||||
useTouchEvents?: boolean;
|
||||
|
||||
/**
|
||||
* Enable useTouchEvents automatically when touch device is detected.
|
||||
*/
|
||||
autoUseTouchEvents?: boolean;
|
||||
|
||||
/**
|
||||
* Opt out of PointerEvents handling, falling back to the prior mouse event logic.
|
||||
*/
|
||||
useMouseEvents?: boolean;
|
||||
|
||||
/**
|
||||
* Disable button hold action.
|
||||
*/
|
||||
disableButtonHold?: boolean;
|
||||
|
||||
/**
|
||||
* Adds unicode right-to-left control characters to input return values.
|
||||
*/
|
||||
rtl?: boolean;
|
||||
|
||||
/**
|
||||
* Executes the callback function on key press. Returns button layout name (i.e.: "{shift}").
|
||||
*/
|
||||
onKeyPress?: (button: string) => any;
|
||||
|
||||
/**
|
||||
* Executes the callback function on key release.
|
||||
*/
|
||||
onKeyReleased?: (button: string) => any;
|
||||
|
||||
/**
|
||||
* Executes the callback function on input change. Returns the current input's string.
|
||||
*/
|
||||
onChange?: (input: string) => any;
|
||||
|
||||
/**
|
||||
* Executes the callback function before the first simple-keyboard render.
|
||||
*/
|
||||
beforeFirstRender?: () => void;
|
||||
|
||||
/**
|
||||
* Executes the callback function before a simple-keyboard render.
|
||||
*/
|
||||
beforeRender?: () => void;
|
||||
|
||||
/**
|
||||
* Executes the callback function every time simple-keyboard is rendered (e.g: when you change layouts).
|
||||
*/
|
||||
onRender?: () => void;
|
||||
|
||||
/**
|
||||
* Executes the callback function once simple-keyboard is rendered for the first time (on initialization).
|
||||
*/
|
||||
onInit?: () => void;
|
||||
|
||||
/**
|
||||
* Executes the callback function on input change. Returns the input object with all defined inputs.
|
||||
*/
|
||||
onChangeAll?: (inputs: any) => any;
|
||||
|
||||
/**
|
||||
* Module classes to be loaded by simple-keyboard.
|
||||
*/
|
||||
modules?: any[];
|
||||
|
||||
/**
|
||||
* Module options can have any format
|
||||
*/
|
||||
[name: string]: any;
|
||||
}
|
||||
|
||||
class Keyboard {
|
||||
constructor(selector: string, options: KeyboardOptions);
|
||||
constructor(selector: HTMLDivElement, options: KeyboardOptions);
|
||||
constructor(options: KeyboardOptions);
|
||||
|
||||
/**
|
||||
* Options
|
||||
*/
|
||||
options: KeyboardOptions;
|
||||
|
||||
/**
|
||||
* Utilities
|
||||
*/
|
||||
utilities?: any;
|
||||
|
||||
/**
|
||||
* caretPosition
|
||||
*/
|
||||
caretPosition?: number;
|
||||
|
||||
|
||||
/**
|
||||
* caretPositionEnd
|
||||
*/
|
||||
caretPositionEnd?: number;
|
||||
|
||||
/**
|
||||
* Changes the internal caret position
|
||||
* @param {number} position The caret's start position
|
||||
* @param {number} positionEnd The caret's end position
|
||||
*/
|
||||
setCaretPosition(position: number, positionEnd?: number): void;
|
||||
|
||||
/**
|
||||
* Retrieves the internal caret position
|
||||
*/
|
||||
getCaretPosition(): number;
|
||||
|
||||
/**
|
||||
* Retrieves the internal end caret position
|
||||
*/
|
||||
getCaretPositionEnd(): number;
|
||||
|
||||
/**
|
||||
* Adds/Modifies an entry to the `buttonTheme`. Basically a way to add a class to a button.
|
||||
* @param {string} buttons List of buttons to select (separated by a space).
|
||||
* @param {string} className Classes to give to the selected buttons (separated by space).
|
||||
*/
|
||||
addButtonTheme(buttons: string, className: string): void;
|
||||
|
||||
/**
|
||||
* Removes/Amends an entry to the `buttonTheme`. Basically a way to remove a class previously added to a button through buttonTheme or addButtonTheme.
|
||||
* @param {string} buttons List of buttons to select (separated by a space).
|
||||
* @param {string} className Classes to give to the selected buttons (separated by space).
|
||||
*/
|
||||
removeButtonTheme(buttons: string, className: string): void;
|
||||
|
||||
/**
|
||||
* Clear the keyboard's input.
|
||||
*
|
||||
* @param {string} [inputName] optional - the internal input to select
|
||||
*/
|
||||
clearInput(inputName?: string): void;
|
||||
|
||||
/**
|
||||
* Get the keyboard’s input (You can also get it from the onChange prop).
|
||||
* @param {string} [inputName] optional - the internal input to select
|
||||
*/
|
||||
getInput(inputName?: string): string;
|
||||
|
||||
/**
|
||||
* Set the keyboard’s input.
|
||||
* @param {string} input the input value
|
||||
* @param {string} inputName optional - the internal input to select
|
||||
*/
|
||||
setInput(input: string, inputName?: string): void;
|
||||
|
||||
/**
|
||||
* Replaces the entire internal input object.
|
||||
* @param {string} input the input object
|
||||
*/
|
||||
replaceInput(input: any): void;
|
||||
|
||||
/**
|
||||
* Set new option or modify existing ones after initialization.
|
||||
* @param {KeyboardOptions} option The option to set
|
||||
*/
|
||||
setOptions(options: KeyboardOptions): void;
|
||||
|
||||
/**
|
||||
* Send a command to all simple-keyboard instances at once (if you have multiple instances).
|
||||
* @param {function(instance: object, key: string)} callback Function to run on every instance
|
||||
*/
|
||||
dispatch(callback: (instance: any, key: string) => void): void;
|
||||
|
||||
/**
|
||||
* Get the DOM Element of a button. If there are several buttons with the same name, an array of the DOM Elements is returned.
|
||||
* @param {string} button The button layout name to select
|
||||
*/
|
||||
getButtonElement(button: string): HTMLElement | HTMLElement[];
|
||||
|
||||
/**
|
||||
* Clears keyboard listeners and DOM elements.
|
||||
*/
|
||||
destroy(): void;
|
||||
|
||||
/**
|
||||
* Iterate on each button (ForEach)
|
||||
*/
|
||||
recurseButtons(fn: any): void;
|
||||
}
|
||||
|
||||
export default Keyboard;
|
||||
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 element’s maxlengthattribute.
|
||||
*
|
||||
* `{ [inputName: string]: number }`: Restrains simple-keyboard’s individual inputs to a certain length. This should be used in addition to the input element’s 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 keyboard’s input.
|
||||
* @param {string} [inputName] optional - the internal input to select
|
||||
*/
|
||||
clearInput(inputName: string): void;
|
||||
/**
|
||||
* Get the keyboard’s 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 keyboard’s 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
101
config/env.js
101
config/env.js
@ -1,101 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Make sure that including paths.js after env.js will read .env variables.
|
||||
delete require.cache[require.resolve('./paths')];
|
||||
|
||||
const NODE_ENV = process.env.NODE_ENV;
|
||||
if (!NODE_ENV) {
|
||||
throw new Error(
|
||||
'The NODE_ENV environment variable is required but was not specified.'
|
||||
);
|
||||
}
|
||||
|
||||
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
|
||||
const dotenvFiles = [
|
||||
`${paths.dotenv}.${NODE_ENV}.local`,
|
||||
`${paths.dotenv}.${NODE_ENV}`,
|
||||
// Don't include `.env.local` for `test` environment
|
||||
// since normally you expect tests to produce the same
|
||||
// results for everyone
|
||||
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
|
||||
paths.dotenv,
|
||||
].filter(Boolean);
|
||||
|
||||
// Load environment variables from .env* files. Suppress warnings using silent
|
||||
// if this file is missing. dotenv will never modify any environment variables
|
||||
// that have already been set. Variable expansion is supported in .env files.
|
||||
// https://github.com/motdotla/dotenv
|
||||
// https://github.com/motdotla/dotenv-expand
|
||||
dotenvFiles.forEach(dotenvFile => {
|
||||
if (fs.existsSync(dotenvFile)) {
|
||||
require('dotenv-expand')(
|
||||
require('dotenv').config({
|
||||
path: dotenvFile,
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// We support resolving modules according to `NODE_PATH`.
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
// https://github.com/facebook/create-react-app/issues/253.
|
||||
// It works similar to `NODE_PATH` in Node itself:
|
||||
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
|
||||
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
|
||||
// Otherwise, we risk importing Node.js core modules into an app instead of webpack shims.
|
||||
// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
|
||||
// We also resolve them to make sure all tools using them work consistently.
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
process.env.NODE_PATH = (process.env.NODE_PATH || '')
|
||||
.split(path.delimiter)
|
||||
.filter(folder => folder && !path.isAbsolute(folder))
|
||||
.map(folder => path.resolve(appDirectory, folder))
|
||||
.join(path.delimiter);
|
||||
|
||||
// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
|
||||
// injected into the application via DefinePlugin in webpack configuration.
|
||||
const REACT_APP = /^REACT_APP_/i;
|
||||
|
||||
function getClientEnvironment(publicUrl) {
|
||||
const raw = Object.keys(process.env)
|
||||
.filter(key => REACT_APP.test(key))
|
||||
.reduce(
|
||||
(env, key) => {
|
||||
env[key] = process.env[key];
|
||||
return env;
|
||||
},
|
||||
{
|
||||
// Useful for determining whether we’re running in production mode.
|
||||
// Most importantly, it switches React into the correct mode.
|
||||
NODE_ENV: process.env.NODE_ENV || 'development',
|
||||
// Useful for resolving the correct path to static assets in `public`.
|
||||
// For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
|
||||
// This should only be used as an escape hatch. Normally you would put
|
||||
// images into the `src` and `import` them in code to get their paths.
|
||||
PUBLIC_URL: publicUrl,
|
||||
// We support configuring the sockjs pathname during development.
|
||||
// These settings let a developer run multiple simultaneous projects.
|
||||
// They are used as the connection `hostname`, `pathname` and `port`
|
||||
// in webpackHotDevClient. They are used as the `sockHost`, `sockPath`
|
||||
// and `sockPort` options in webpack-dev-server.
|
||||
WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
|
||||
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
|
||||
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
|
||||
}
|
||||
);
|
||||
// Stringify all values so we can feed into webpack DefinePlugin
|
||||
const stringified = {
|
||||
'process.env': Object.keys(raw).reduce((env, key) => {
|
||||
env[key] = JSON.stringify(raw[key]);
|
||||
return env;
|
||||
}, {}),
|
||||
};
|
||||
|
||||
return { raw, stringified };
|
||||
}
|
||||
|
||||
module.exports = getClientEnvironment;
|
@ -1,66 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const paths = require('./paths');
|
||||
|
||||
// Ensure the certificate and key provided are valid and if not
|
||||
// throw an easy to debug error
|
||||
function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
|
||||
let encrypted;
|
||||
try {
|
||||
// publicEncrypt will throw an error with an invalid cert
|
||||
encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// privateDecrypt will throw an error with an invalid key
|
||||
crypto.privateDecrypt(key, encrypted);
|
||||
} catch (err) {
|
||||
throw new Error(
|
||||
`The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
|
||||
err.message
|
||||
}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Read file and throw an error if it doesn't exist
|
||||
function readEnvFile(file, type) {
|
||||
if (!fs.existsSync(file)) {
|
||||
throw new Error(
|
||||
`You specified ${chalk.cyan(
|
||||
type
|
||||
)} in your env, but the file "${chalk.yellow(file)}" can't be found.`
|
||||
);
|
||||
}
|
||||
return fs.readFileSync(file);
|
||||
}
|
||||
|
||||
// Get the https config
|
||||
// Return cert files if provided in env, otherwise just true or false
|
||||
function getHttpsConfig() {
|
||||
const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
|
||||
const isHttps = HTTPS === 'true';
|
||||
|
||||
if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
|
||||
const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
|
||||
const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
|
||||
const config = {
|
||||
cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
|
||||
key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
|
||||
};
|
||||
|
||||
validateKeyAndCerts({ ...config, keyFile, crtFile });
|
||||
return config;
|
||||
}
|
||||
return isHttps;
|
||||
}
|
||||
|
||||
module.exports = getHttpsConfig;
|
@ -1,14 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// This is a custom Jest transformer turning style imports into empty objects.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
|
||||
module.exports = {
|
||||
process() {
|
||||
return 'module.exports = {};';
|
||||
},
|
||||
getCacheKey() {
|
||||
// The output is always the same.
|
||||
return 'cssTransform';
|
||||
},
|
||||
};
|
@ -1,40 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const camelcase = require('camelcase');
|
||||
|
||||
// This is a custom Jest transformer turning file imports into filenames.
|
||||
// http://facebook.github.io/jest/docs/en/webpack.html
|
||||
|
||||
module.exports = {
|
||||
process(src, filename) {
|
||||
const assetFilename = JSON.stringify(path.basename(filename));
|
||||
|
||||
if (filename.match(/\.svg$/)) {
|
||||
// Based on how SVGR generates a component name:
|
||||
// https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
|
||||
const pascalCaseFilename = camelcase(path.parse(filename).name, {
|
||||
pascalCase: true,
|
||||
});
|
||||
const componentName = `Svg${pascalCaseFilename}`;
|
||||
return `const React = require('react');
|
||||
module.exports = {
|
||||
__esModule: true,
|
||||
default: ${assetFilename},
|
||||
ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
|
||||
return {
|
||||
$$typeof: Symbol.for('react.element'),
|
||||
type: 'svg',
|
||||
ref: ref,
|
||||
key: null,
|
||||
props: Object.assign({}, props, {
|
||||
children: ${assetFilename}
|
||||
})
|
||||
};
|
||||
}),
|
||||
};`;
|
||||
}
|
||||
|
||||
return `module.exports = ${assetFilename};`;
|
||||
},
|
||||
};
|
@ -1,141 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const paths = require('./paths');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const resolve = require('resolve');
|
||||
|
||||
/**
|
||||
* Get additional module paths based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {Object} options
|
||||
*/
|
||||
function getAdditionalModulePaths(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
// We need to explicitly check for null and undefined (and not a falsy value) because
|
||||
// TypeScript treats an empty string as `.`.
|
||||
if (baseUrl == null) {
|
||||
// If there's no baseUrl set we respect NODE_PATH
|
||||
// Note that NODE_PATH is deprecated and will be removed
|
||||
// in the next major release of create-react-app.
|
||||
|
||||
const nodePath = process.env.NODE_PATH || '';
|
||||
return nodePath.split(path.delimiter).filter(Boolean);
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
// We don't need to do anything if `baseUrl` is set to `node_modules`. This is
|
||||
// the default behavior.
|
||||
if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Allow the user set the `baseUrl` to `appSrc`.
|
||||
if (path.relative(paths.appSrc, baseUrlResolved) === '') {
|
||||
return [paths.appSrc];
|
||||
}
|
||||
|
||||
// If the path is equal to the root directory we ignore it here.
|
||||
// We don't want to allow importing from the root directly as source files are
|
||||
// not transpiled outside of `src`. We do allow importing them with the
|
||||
// absolute path (e.g. `src/Components/Button.js`) but we set that up with
|
||||
// an alias.
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Otherwise, throw an error.
|
||||
throw new Error(
|
||||
chalk.red.bold(
|
||||
"Your project's `baseUrl` can only be set to `src` or `node_modules`." +
|
||||
' Create React App does not support other values at this time.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get webpack aliases based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {*} options
|
||||
*/
|
||||
function getWebpackAliases(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return {
|
||||
src: paths.appSrc,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get jest aliases based on the baseUrl of a compilerOptions object.
|
||||
*
|
||||
* @param {*} options
|
||||
*/
|
||||
function getJestAliases(options = {}) {
|
||||
const baseUrl = options.baseUrl;
|
||||
|
||||
if (!baseUrl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
|
||||
|
||||
if (path.relative(paths.appPath, baseUrlResolved) === '') {
|
||||
return {
|
||||
'^src/(.*)$': '<rootDir>/src/$1',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getModules() {
|
||||
// Check if TypeScript is setup
|
||||
const hasTsConfig = fs.existsSync(paths.appTsConfig);
|
||||
const hasJsConfig = fs.existsSync(paths.appJsConfig);
|
||||
|
||||
if (hasTsConfig && hasJsConfig) {
|
||||
throw new Error(
|
||||
'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
|
||||
);
|
||||
}
|
||||
|
||||
let config;
|
||||
|
||||
// If there's a tsconfig.json we assume it's a
|
||||
// TypeScript project and set up the config
|
||||
// based on tsconfig.json
|
||||
if (hasTsConfig) {
|
||||
const ts = require(resolve.sync('typescript', {
|
||||
basedir: paths.appNodeModules,
|
||||
}));
|
||||
config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
|
||||
// Otherwise we'll check if there is jsconfig.json
|
||||
// for non TS projects.
|
||||
} else if (hasJsConfig) {
|
||||
config = require(paths.appJsConfig);
|
||||
}
|
||||
|
||||
config = config || {};
|
||||
const options = config.compilerOptions || {};
|
||||
|
||||
const additionalModulePaths = getAdditionalModulePaths(options);
|
||||
|
||||
return {
|
||||
additionalModulePaths: additionalModulePaths,
|
||||
webpackAliases: getWebpackAliases(options),
|
||||
jestAliases: getJestAliases(options),
|
||||
hasTsConfig,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = getModules();
|
@ -1,73 +0,0 @@
|
||||
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
|
||||
|
||||
// Make sure any symlinks in the project folder are resolved:
|
||||
// https://github.com/facebook/create-react-app/issues/637
|
||||
const appDirectory = fs.realpathSync(process.cwd());
|
||||
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
|
||||
|
||||
|
||||
// We use `PUBLIC_URL` environment variable or "homepage" field to infer
|
||||
// "public path" at which the app is served.
|
||||
// webpack needs to know it to put the right <script> hrefs into HTML even in
|
||||
// single-page apps that may serve index.html for nested URLs like /todos/42.
|
||||
// We can't use a relative path in HTML because we don't want to load something
|
||||
// like /todos/42/static/js/bundle.7289d.js. We have to know the root.
|
||||
const publicUrlOrPath = '/';
|
||||
|
||||
const moduleFileExtensions = [
|
||||
'web.mjs',
|
||||
'mjs',
|
||||
'web.js',
|
||||
'js',
|
||||
'web.ts',
|
||||
'ts',
|
||||
'web.tsx',
|
||||
'tsx',
|
||||
'json',
|
||||
'web.jsx',
|
||||
'jsx',
|
||||
];
|
||||
|
||||
// Resolve file paths in the same order as webpack
|
||||
const resolveModule = (resolveFn, filePath) => {
|
||||
const extension = moduleFileExtensions.find(extension =>
|
||||
fs.existsSync(resolveFn(`${filePath}.${extension}`))
|
||||
);
|
||||
|
||||
if (extension) {
|
||||
return resolveFn(`${filePath}.${extension}`);
|
||||
}
|
||||
|
||||
return resolveFn(`${filePath}.js`);
|
||||
};
|
||||
|
||||
// config after eject: we're in ./config/
|
||||
module.exports = {
|
||||
dotenv: resolveApp('.env'),
|
||||
appPath: resolveApp('.'),
|
||||
appBuild: resolveApp('build'),
|
||||
appDemo: resolveApp('demo'),
|
||||
appPublic: resolveApp('public'),
|
||||
appHtml: resolveApp('public/index.html'),
|
||||
appIndexJs: resolveModule(resolveApp, 'src/demo/index'),
|
||||
appLibIndexJs: resolveModule(resolveApp, 'src/lib/index'),
|
||||
appDemoIndexJs: resolveModule(resolveApp, 'src/demo/index'),
|
||||
appPackageJson: resolveApp('package.json'),
|
||||
appSrc: resolveApp('src'),
|
||||
appSrcLib: resolveApp('src/lib'),
|
||||
appSrcLibTypes: resolveApp('src/lib/@types'),
|
||||
appSrcDemo: resolveApp('src/demo'),
|
||||
appTsConfig: resolveApp('tsconfig.json'),
|
||||
appJsConfig: resolveApp('jsconfig.json'),
|
||||
yarnLockFile: resolveApp('yarn.lock'),
|
||||
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
|
||||
proxySetup: resolveApp('src/setupProxy.js'),
|
||||
appNodeModules: resolveApp('node_modules'),
|
||||
publicUrlOrPath
|
||||
};
|
||||
|
||||
module.exports.moduleFileExtensions = moduleFileExtensions;
|
@ -1,35 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const { resolveModuleName } = require('ts-pnp');
|
||||
|
||||
exports.resolveModuleName = (
|
||||
typescript,
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost
|
||||
) => {
|
||||
return resolveModuleName(
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost,
|
||||
typescript.resolveModuleName
|
||||
);
|
||||
};
|
||||
|
||||
exports.resolveTypeReferenceDirective = (
|
||||
typescript,
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost
|
||||
) => {
|
||||
return resolveModuleName(
|
||||
moduleName,
|
||||
containingFile,
|
||||
compilerOptions,
|
||||
resolutionHost,
|
||||
typescript.resolveTypeReferenceDirective
|
||||
);
|
||||
};
|
@ -1,674 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const resolve = require('resolve');
|
||||
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
|
||||
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
const safePostCssParser = require('postcss-safe-parser');
|
||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
|
||||
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
|
||||
const paths = require('./paths');
|
||||
const modules = require('./modules');
|
||||
const getClientEnvironment = require('./env');
|
||||
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
|
||||
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
|
||||
const getPackageJson = require('./getPackageJson');
|
||||
const eslint = require('eslint');
|
||||
|
||||
const postcssNormalize = require('postcss-normalize');
|
||||
|
||||
const appPackageJson = require(paths.appPackageJson);
|
||||
|
||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
||||
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
|
||||
// makes for a smoother build process.
|
||||
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
|
||||
|
||||
const isExtendingEslintConfig = process.env.EXTEND_ESLINT === 'true';
|
||||
|
||||
const imageInlineSizeLimit = parseInt(
|
||||
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
|
||||
);
|
||||
|
||||
// Check if TypeScript is setup
|
||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
||||
|
||||
// style files regexes
|
||||
const cssRegex = /\.css$/;
|
||||
const cssModuleRegex = /\.module\.css$/;
|
||||
const sassRegex = /\.(scss|sass)$/;
|
||||
const sassModuleRegex = /\.module\.(scss|sass)$/;
|
||||
|
||||
// This is the production and development configuration.
|
||||
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
|
||||
module.exports = function(webpackEnv) {
|
||||
const isEnvDevelopment = webpackEnv === 'development';
|
||||
const isEnvProduction = webpackEnv === 'production';
|
||||
|
||||
// Variable used for enabling profiling in Production
|
||||
// passed into alias object. Uses a flag if passed into the build command
|
||||
const isEnvProductionProfile =
|
||||
isEnvProduction && process.argv.includes('--profile');
|
||||
|
||||
// We will provide `paths.publicUrlOrPath` to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
|
||||
|
||||
// common function to get style loaders
|
||||
const getStyleLoaders = (cssOptions, preProcessor) => {
|
||||
const loaders = [
|
||||
isEnvDevelopment && require.resolve('style-loader'),
|
||||
isEnvProduction && {
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
// css is located in `static/css`, use '../../' to locate index.html folder
|
||||
// in production `paths.publicUrlOrPath` can be a relative path
|
||||
options: paths.publicUrlOrPath.startsWith('.')
|
||||
? { publicPath: '../../' }
|
||||
: {},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: cssOptions,
|
||||
},
|
||||
{
|
||||
// Options for PostCSS as we reference these options twice
|
||||
// Adds vendor prefixing based on your specified browser support in
|
||||
// package.json
|
||||
loader: require.resolve('postcss-loader'),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebook/create-react-app/issues/2677
|
||||
ident: 'postcss',
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
require('postcss-preset-env')({
|
||||
autoprefixer: {
|
||||
flexbox: 'no-2009',
|
||||
},
|
||||
stage: 3,
|
||||
}),
|
||||
// Adds PostCSS Normalize as the reset css with default options,
|
||||
// so that it honors browserslist config in package.json
|
||||
// which in turn let's users customize the target behavior as per their needs.
|
||||
postcssNormalize(),
|
||||
],
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
].filter(Boolean);
|
||||
if (preProcessor) {
|
||||
loaders.push(
|
||||
{
|
||||
loader: require.resolve('resolve-url-loader'),
|
||||
options: {
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve(preProcessor),
|
||||
options: {
|
||||
sourceMap: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
return loaders;
|
||||
};
|
||||
|
||||
return {
|
||||
mode: 'production',
|
||||
// Stop compilation early in production
|
||||
bail: isEnvProduction,
|
||||
devtool: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
? 'source-map'
|
||||
: false
|
||||
: isEnvDevelopment && 'cheap-module-source-map',
|
||||
// These are the "entry points" to our application.
|
||||
// This means they will be the "root" imports that are included in JS bundle.
|
||||
entry: isEnvProduction ? [paths.appDemoIndexJs] : [
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
isEnvDevelopment &&
|
||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
// Finally, this is your app's code:
|
||||
paths.appDemoIndexJs,
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
].filter(Boolean),
|
||||
output: {
|
||||
// The build folder.
|
||||
path: isEnvProduction ? paths.appDemo : undefined,
|
||||
// Add /* filename */ comments to generated require()s in the output.
|
||||
pathinfo: isEnvDevelopment,
|
||||
// There will be one main bundle, and one file per asynchronous chunk.
|
||||
// In development, it does not produce real files.
|
||||
filename: 'index.js',
|
||||
// TODO: remove this when upgrading to webpack 5
|
||||
futureEmitAssets: true,
|
||||
// There are also additional JS chunk files if you use code splitting.
|
||||
chunkFilename: isEnvProduction
|
||||
? 'static/js/[name].[contenthash:8].chunk.js'
|
||||
: isEnvDevelopment && 'static/js/[name].chunk.js',
|
||||
// webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: isEnvProduction
|
||||
? info =>
|
||||
path
|
||||
.relative(paths.appSrcDemo, info.absoluteResourcePath)
|
||||
.replace(/\\/g, '/')
|
||||
: isEnvDevelopment &&
|
||||
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
|
||||
// Prevents conflicts when multiple webpack runtimes (from different apps)
|
||||
// are used on the same page.
|
||||
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
|
||||
// this defaults to 'window', but by setting it to 'this' then
|
||||
// module chunks which are built will work in web workers as well.
|
||||
globalObject: 'this',
|
||||
},
|
||||
optimization: {
|
||||
minimize: isEnvProduction,
|
||||
minimizer: [
|
||||
// This is only used in production mode
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
parse: {
|
||||
// We want terser to parse ecma 8 code. However, we don't want it
|
||||
// to apply any minification steps that turns valid ecma 5 code
|
||||
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
||||
// sections only apply transformations that are ecma 5 safe
|
||||
// https://github.com/facebook/create-react-app/pull/4234
|
||||
ecma: 8,
|
||||
},
|
||||
compress: {
|
||||
ecma: 5,
|
||||
warnings: false,
|
||||
// Disabled because of an issue with Uglify breaking seemingly valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/2376
|
||||
// Pending further investigation:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/2011
|
||||
comparisons: false,
|
||||
// Disabled because of an issue with Terser breaking valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/5250
|
||||
// Pending further investigation:
|
||||
// https://github.com/terser-js/terser/issues/120
|
||||
inline: 2,
|
||||
},
|
||||
mangle: {
|
||||
safari10: true,
|
||||
keep_classnames: true,
|
||||
keep_fnames: true,
|
||||
module: true
|
||||
},
|
||||
output: {
|
||||
ecma: 5,
|
||||
comments: false,
|
||||
// Turned on because emoji and regex is not minified properly using default
|
||||
// https://github.com/facebook/create-react-app/issues/2488
|
||||
ascii_only: true,
|
||||
},
|
||||
},
|
||||
cache: true,
|
||||
sourceMap: shouldUseSourceMap,
|
||||
}),
|
||||
new OptimizeCSSAssetsPlugin({
|
||||
cssProcessorOptions: {
|
||||
parser: safePostCssParser,
|
||||
map: shouldUseSourceMap
|
||||
? {
|
||||
// `inline: false` forces the sourcemap to be output into a
|
||||
// separate file
|
||||
inline: false,
|
||||
// `annotation: true` appends the sourceMappingURL to the end of
|
||||
// the css file, helping the browser find the sourcemap
|
||||
annotation: true,
|
||||
}
|
||||
: false,
|
||||
},
|
||||
cssProcessorPluginOptions: {
|
||||
preset: ['default', { minifyFontValues: { removeQuotes: false } }],
|
||||
},
|
||||
}),
|
||||
],
|
||||
// Automatically split vendor and commons
|
||||
// https://twitter.com/wSokra/status/969633336732905474
|
||||
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
|
||||
splitChunks: {
|
||||
cacheGroups: {
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
// Keep the runtime chunk separated to enable long term caching
|
||||
// https://twitter.com/wSokra/status/969679223278505985
|
||||
// https://github.com/facebook/create-react-app/issues/5358
|
||||
runtimeChunk: false,
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebook/create-react-app/issues/253
|
||||
modules: ['node_modules', paths.appNodeModules].concat(
|
||||
modules.additionalModulePaths || []
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebook/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: paths.moduleFileExtensions
|
||||
.map(ext => `.${ext}`)
|
||||
.filter(ext => useTypeScript || !ext.includes('ts')),
|
||||
alias: {
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
// Allows for better profiling with ReactDevTools
|
||||
...(isEnvProductionProfile && {
|
||||
'react-dom$': 'react-dom/profiling',
|
||||
'scheduler/tracing': 'scheduler/tracing-profiling',
|
||||
}),
|
||||
...(modules.webpackAliases || {}),
|
||||
},
|
||||
plugins: [
|
||||
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
|
||||
// guards against forgotten dependencies and such.
|
||||
PnpWebpackPlugin,
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
||||
],
|
||||
},
|
||||
resolveLoader: {
|
||||
plugins: [
|
||||
// Also related to Plug'n'Play, but this time it tells webpack to load its loaders
|
||||
// from the current package.
|
||||
PnpWebpackPlugin.moduleLoader(module),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
// Disable require.ensure as it's not a standard language feature.
|
||||
{ parser: { requireEnsure: false } },
|
||||
|
||||
// First, run the linter.
|
||||
// It's important to do this before Babel processes the JS.
|
||||
{
|
||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
enforce: 'pre',
|
||||
use: [
|
||||
{
|
||||
options: {
|
||||
cache: true,
|
||||
formatter: require.resolve('react-dev-utils/eslintFormatter'),
|
||||
eslintPath: require.resolve('eslint'),
|
||||
resolvePluginsRelativeTo: __dirname,
|
||||
|
||||
},
|
||||
loader: require.resolve('eslint-loader'),
|
||||
},
|
||||
],
|
||||
include: paths.appSrcDemo,
|
||||
},
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// "url" loader works like "file" loader except that it embeds assets
|
||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
||||
// A missing `test` is equivalent to a match.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: imageInlineSizeLimit,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// Process application JS with Babel.
|
||||
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
|
||||
{
|
||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
include: paths.appSrcDemo,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
customize: require.resolve(
|
||||
'babel-preset-react-app/webpack-overrides'
|
||||
),
|
||||
|
||||
plugins: [
|
||||
[
|
||||
require.resolve('babel-plugin-named-asset-import'),
|
||||
{
|
||||
loaderMap: {
|
||||
svg: {
|
||||
ReactComponent:
|
||||
'@svgr/webpack?-svgo,+titleProp,+ref![path]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
||||
// directory for faster rebuilds.
|
||||
cacheDirectory: true,
|
||||
// See #6846 for context on why cacheCompression is disabled
|
||||
cacheCompression: false,
|
||||
compact: isEnvProduction,
|
||||
},
|
||||
},
|
||||
// Process any JS outside of the app with Babel.
|
||||
// Unlike the application JS, we only compile the standard ES features.
|
||||
{
|
||||
test: /\.(js|mjs)$/,
|
||||
exclude: /@babel(?:\/|\\{1,2})runtime/,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
compact: false,
|
||||
presets: [
|
||||
[
|
||||
require.resolve('babel-preset-react-app/dependencies'),
|
||||
{ helpers: true },
|
||||
],
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react"
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
]
|
||||
],
|
||||
cacheDirectory: true,
|
||||
// See #6846 for context on why cacheCompression is disabled
|
||||
cacheCompression: false,
|
||||
|
||||
// Babel sourcemaps are needed for debugging into node_modules
|
||||
// code. Without the options below, debuggers like VSCode
|
||||
// show incorrect code and set breakpoints on the wrong lines.
|
||||
sourceMaps: shouldUseSourceMap,
|
||||
inputSourceMap: shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
||||
// In production, we use MiniCSSExtractPlugin to extract that CSS
|
||||
// to a file, but in development "style" loader enables hot editing
|
||||
// of CSS.
|
||||
// By default we support CSS Modules with the extension .module.css
|
||||
{
|
||||
test: cssRegex,
|
||||
exclude: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
}),
|
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true,
|
||||
},
|
||||
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
|
||||
// using the extension .module.css
|
||||
{
|
||||
test: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
}),
|
||||
},
|
||||
// Opt-in support for SASS (using .scss or .sass extensions).
|
||||
// By default we support SASS Modules with the
|
||||
// extensions .module.scss or .module.sass
|
||||
{
|
||||
test: sassRegex,
|
||||
exclude: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true,
|
||||
},
|
||||
// Adds support for CSS Modules, but using SASS
|
||||
// using the extension .module.scss or .module.sass
|
||||
{
|
||||
test: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
},
|
||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||
// When you `import` an asset, you get its (virtual) filename.
|
||||
// In production, they would get copied to the `build` folder.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// its runtime that would otherwise be processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin(
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
inject: true,
|
||||
template: paths.appHtml,
|
||||
},
|
||||
isEnvProduction
|
||||
? {
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
),
|
||||
// Inlines the webpack runtime script. This script is too small to warrant
|
||||
// a network request.
|
||||
// https://github.com/facebook/create-react-app/issues/5358
|
||||
isEnvProduction &&
|
||||
shouldInlineRuntimeChunk &&
|
||||
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// It will be an empty string unless you specify "homepage"
|
||||
// in `package.json`, in which case it will be the pathname of that URL.
|
||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
|
||||
// This gives some necessary context to module not found errors, such as
|
||||
// the requesting resource.
|
||||
new ModuleNotFoundPlugin(paths.appPath),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
||||
// It is absolutely essential that NODE_ENV is set to production
|
||||
// during a production build.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// This is necessary to emit hot updates (currently CSS only):
|
||||
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
|
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
// a plugin that prints an error when you attempt to do this.
|
||||
// See https://github.com/facebook/create-react-app/issues/240
|
||||
isEnvDevelopment && new CaseSensitivePathsPlugin(),
|
||||
// If you require a missing module and then `npm install` it, you still have
|
||||
// to restart the development server for webpack to discover it. This plugin
|
||||
// makes the discovery automatic so you don't have to restart.
|
||||
// See https://github.com/facebook/create-react-app/issues/186
|
||||
isEnvDevelopment &&
|
||||
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
|
||||
isEnvProduction &&
|
||||
new MiniCssExtractPlugin({
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
filename: 'index.css',
|
||||
chunkFilename: 'index.[contenthash:8].chunk.css',
|
||||
}),
|
||||
// Generate an asset manifest file with the following content:
|
||||
// - "files" key: Mapping of all asset filenames to their corresponding
|
||||
// output file so that tools can pick it up without having to parse
|
||||
// `index.html`
|
||||
// - "entrypoints" key: Array of files which are included in `index.html`,
|
||||
// can be used to reconstruct the HTML if necessary
|
||||
/*new ManifestPlugin({
|
||||
fileName: 'asset-manifest.json',
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
generate: (seed, files, entrypoints) => {
|
||||
const manifestFiles = files.reduce((manifest, file) => {
|
||||
manifest[file.name] = file.path;
|
||||
return manifest;
|
||||
}, seed);
|
||||
const entrypointFiles = entrypoints.main.filter(
|
||||
fileName => !fileName.endsWith('.map')
|
||||
);
|
||||
|
||||
return {
|
||||
files: manifestFiles,
|
||||
entrypoints: entrypointFiles,
|
||||
};
|
||||
},
|
||||
}),*/
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
// Generate a service worker script that will precache, and keep up to date,
|
||||
// the HTML & assets that are part of the webpack build.
|
||||
/*isEnvProduction &&
|
||||
new WorkboxWebpackPlugin.GenerateSW({
|
||||
clientsClaim: true,
|
||||
exclude: [/\.map$/, /asset-manifest\.json$/],
|
||||
importWorkboxFrom: 'cdn',
|
||||
navigateFallback: paths.publicUrlOrPath + 'index.html',
|
||||
navigateFallbackBlacklist: [
|
||||
// Exclude URLs starting with /_, as they're likely an API call
|
||||
new RegExp('^/_'),
|
||||
// Exclude any URLs whose last part seems to be a file extension
|
||||
// as they're likely a resource and not a SPA route.
|
||||
// URLs containing a "?" character won't be blacklisted as they're likely
|
||||
// a route with query params (e.g. auth callbacks).
|
||||
new RegExp('/[^/?]+\\.[^/]+$'),
|
||||
],
|
||||
}),*/
|
||||
// TypeScript type checking
|
||||
useTypeScript &&
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: resolve.sync('typescript', {
|
||||
basedir: paths.appNodeModules,
|
||||
}),
|
||||
async: isEnvDevelopment,
|
||||
useTypescriptIncrementalApi: true,
|
||||
checkSyntacticErrors: true,
|
||||
resolveModuleNameModule: process.versions.pnp
|
||||
? `${__dirname}/pnpTs.js`
|
||||
: undefined,
|
||||
resolveTypeReferenceDirectiveModule: process.versions.pnp
|
||||
? `${__dirname}/pnpTs.js`
|
||||
: undefined,
|
||||
tsconfig: paths.appTsConfig,
|
||||
reportFiles: [
|
||||
'**',
|
||||
'!**/__tests__/**',
|
||||
'!**/?(*.)(spec|test).*',
|
||||
'!**/src/setupProxy.*',
|
||||
'!**/src/setupTests.*',
|
||||
],
|
||||
watch: paths.appSrcDemo,
|
||||
silent: true,
|
||||
// The formatter is invoked directly in WebpackDevServerUtils during development
|
||||
formatter: isEnvProduction ? typescriptFormatter : undefined,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
module: 'empty',
|
||||
dgram: 'empty',
|
||||
dns: 'mock',
|
||||
fs: 'empty',
|
||||
http2: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty',
|
||||
},
|
||||
// Turn off performance processing because we utilize
|
||||
// our own hints via the FileSizeReporter
|
||||
performance: false,
|
||||
};
|
||||
};
|
@ -1,712 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const resolve = require('resolve');
|
||||
const PnpWebpackPlugin = require('pnp-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
|
||||
const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
|
||||
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
|
||||
const safePostCssParser = require('postcss-safe-parser');
|
||||
const ManifestPlugin = require('webpack-manifest-plugin');
|
||||
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
|
||||
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
|
||||
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
|
||||
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
|
||||
const paths = require('./paths');
|
||||
const modules = require('./modules');
|
||||
const getClientEnvironment = require('./env');
|
||||
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
|
||||
const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin');
|
||||
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
const getPackageJson = require('./getPackageJson');
|
||||
const PrettierPlugin = require("prettier-webpack-plugin");
|
||||
const eslint = require('eslint');
|
||||
const postcssNormalize = require('postcss-normalize');
|
||||
const appPackageJson = require(paths.appPackageJson);
|
||||
|
||||
// Source maps are resource heavy and can cause out of memory issue for large source files.
|
||||
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
|
||||
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
|
||||
// makes for a smoother build process.
|
||||
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
|
||||
|
||||
const isExtendingEslintConfig = process.env.EXTEND_ESLINT === 'true';
|
||||
|
||||
const imageInlineSizeLimit = parseInt(
|
||||
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
|
||||
);
|
||||
|
||||
// Check if TypeScript is setup
|
||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
||||
|
||||
// style files regexes
|
||||
const cssRegex = /\.css$/;
|
||||
const cssModuleRegex = /\.module\.css$/;
|
||||
const sassRegex = /\.(scss|sass)$/;
|
||||
const sassModuleRegex = /\.module\.(scss|sass)$/;
|
||||
|
||||
const {
|
||||
version,
|
||||
name,
|
||||
license,
|
||||
repository,
|
||||
author,
|
||||
} = getPackageJson('version', 'name', 'license', 'repository', 'author');
|
||||
|
||||
const banner = `
|
||||
${name} v${version}
|
||||
${repository.url}
|
||||
|
||||
Copyright (c) ${author.replace(/ *<[^)]*> */g, " ")}
|
||||
|
||||
This source code is licensed under the ${license} license found in the
|
||||
LICENSE file in the root directory of this source tree.
|
||||
`;
|
||||
|
||||
// This is the production and development configuration.
|
||||
// It is focused on developer experience, fast rebuilds, and a minimal bundle.
|
||||
module.exports = function(webpackEnv) {
|
||||
const isEnvDevelopment = webpackEnv === 'development';
|
||||
const isEnvProduction = webpackEnv === 'production';
|
||||
|
||||
// Variable used for enabling profiling in Production
|
||||
// passed into alias object. Uses a flag if passed into the build command
|
||||
const isEnvProductionProfile =
|
||||
isEnvProduction && process.argv.includes('--profile');
|
||||
|
||||
// We will provide `paths.publicUrlOrPath` to our app
|
||||
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
|
||||
// Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
|
||||
// Get environment variables to inject into our app.
|
||||
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
|
||||
|
||||
// common function to get style loaders
|
||||
const getStyleLoaders = (cssOptions, preProcessor) => {
|
||||
const loaders = [
|
||||
isEnvDevelopment && require.resolve('style-loader'),
|
||||
isEnvProduction && {
|
||||
loader: MiniCssExtractPlugin.loader,
|
||||
// css is located in `static/css`, use '../../' to locate index.html folder
|
||||
// in production `paths.publicUrlOrPath` can be a relative path
|
||||
options: paths.publicUrlOrPath.startsWith('.')
|
||||
? { publicPath: '../../' }
|
||||
: {},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: cssOptions,
|
||||
},
|
||||
{
|
||||
// Options for PostCSS as we reference these options twice
|
||||
// Adds vendor prefixing based on your specified browser support in
|
||||
// package.json
|
||||
loader: require.resolve('postcss-loader'),
|
||||
options: {
|
||||
// Necessary for external CSS imports to work
|
||||
// https://github.com/facebook/create-react-app/issues/2677
|
||||
ident: 'postcss',
|
||||
plugins: () => [
|
||||
require('postcss-flexbugs-fixes'),
|
||||
require('postcss-preset-env')({
|
||||
autoprefixer: {
|
||||
flexbox: 'no-2009',
|
||||
},
|
||||
stage: 3,
|
||||
}),
|
||||
// Adds PostCSS Normalize as the reset css with default options,
|
||||
// so that it honors browserslist config in package.json
|
||||
// which in turn let's users customize the target behavior as per their needs.
|
||||
postcssNormalize(),
|
||||
],
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
].filter(Boolean);
|
||||
if (preProcessor) {
|
||||
loaders.push(
|
||||
{
|
||||
loader: require.resolve('resolve-url-loader'),
|
||||
options: {
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve(preProcessor),
|
||||
options: {
|
||||
sourceMap: true,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
return loaders;
|
||||
};
|
||||
|
||||
return {
|
||||
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
|
||||
// Stop compilation early in production
|
||||
bail: isEnvProduction,
|
||||
devtool: isEnvProduction
|
||||
? shouldUseSourceMap
|
||||
? 'source-map'
|
||||
: false
|
||||
: isEnvDevelopment && 'cheap-module-source-map',
|
||||
// These are the "entry points" to our application.
|
||||
// This means they will be the "root" imports that are included in JS bundle.
|
||||
entry: isEnvProduction ? [paths.appLibIndexJs] : [
|
||||
// Include an alternative client for WebpackDevServer. A client's job is to
|
||||
// connect to WebpackDevServer by a socket and get notified about changes.
|
||||
// When you save a file, the client will either apply hot updates (in case
|
||||
// of CSS changes), or refresh the page (in case of JS changes). When you
|
||||
// make a syntax error, this client will display a syntax error overlay.
|
||||
// Note: instead of the default WebpackDevServer client, we use a custom one
|
||||
// to bring better experience for Create React App users. You can replace
|
||||
// the line below with these two lines if you prefer the stock client:
|
||||
// require.resolve('webpack-dev-server/client') + '?/',
|
||||
// require.resolve('webpack/hot/dev-server'),
|
||||
isEnvDevelopment &&
|
||||
require.resolve('react-dev-utils/webpackHotDevClient'),
|
||||
// Finally, this is your app's code:
|
||||
paths.appIndexJs,
|
||||
// We include the app code last so that if there is a runtime error during
|
||||
// initialization, it doesn't blow up the WebpackDevServer client, and
|
||||
// changing JS code would still trigger a refresh.
|
||||
].filter(Boolean),
|
||||
output: {
|
||||
// The build folder.
|
||||
path: isEnvProduction ? paths.appBuild : undefined,
|
||||
// Add /* filename */ comments to generated require()s in the output.
|
||||
pathinfo: isEnvDevelopment,
|
||||
// There will be one main bundle, and one file per asynchronous chunk.
|
||||
// In development, it does not produce real files.
|
||||
filename: isEnvProduction
|
||||
? 'index.js'
|
||||
: isEnvDevelopment && 'static/js/bundle.js',
|
||||
// TODO: remove this when upgrading to webpack 5
|
||||
futureEmitAssets: true,
|
||||
// There are also additional JS chunk files if you use code splitting.
|
||||
chunkFilename: isEnvProduction
|
||||
? 'static/js/[name].[contenthash:8].chunk.js'
|
||||
: isEnvDevelopment && 'static/js/[name].chunk.js',
|
||||
// webpack uses `publicPath` to determine where the app is being served from.
|
||||
// It requires a trailing slash, or the file assets will get an incorrect path.
|
||||
// We inferred the "public path" (such as / or /my-project) from homepage.
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
library: "SimpleKeyboard",
|
||||
libraryTarget: 'umd',
|
||||
umdNamedDefine: true,
|
||||
// Point sourcemap entries to original disk location (format as URL on Windows)
|
||||
devtoolModuleFilenameTemplate: isEnvProduction
|
||||
? info =>
|
||||
path
|
||||
.relative(paths.appSrcLib, info.absoluteResourcePath)
|
||||
.replace(/\\/g, '/')
|
||||
: isEnvDevelopment &&
|
||||
(info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
|
||||
// Prevents conflicts when multiple webpack runtimes (from different apps)
|
||||
// are used on the same page.
|
||||
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
|
||||
// this defaults to 'window', but by setting it to 'this' then
|
||||
// module chunks which are built will work in web workers as well.
|
||||
globalObject: 'this',
|
||||
},
|
||||
optimization: {
|
||||
minimize: isEnvProduction,
|
||||
minimizer: [
|
||||
// This is only used in production mode
|
||||
new TerserPlugin({
|
||||
terserOptions: {
|
||||
parse: {
|
||||
// We want terser to parse ecma 8 code. However, we don't want it
|
||||
// to apply any minification steps that turns valid ecma 5 code
|
||||
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
||||
// sections only apply transformations that are ecma 5 safe
|
||||
// https://github.com/facebook/create-react-app/pull/4234
|
||||
ecma: 8,
|
||||
},
|
||||
compress: {
|
||||
ecma: 5,
|
||||
warnings: false,
|
||||
// Disabled because of an issue with Uglify breaking seemingly valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/2376
|
||||
// Pending further investigation:
|
||||
// https://github.com/mishoo/UglifyJS2/issues/2011
|
||||
comparisons: false,
|
||||
// Disabled because of an issue with Terser breaking valid code:
|
||||
// https://github.com/facebook/create-react-app/issues/5250
|
||||
// Pending further investigation:
|
||||
// https://github.com/terser-js/terser/issues/120
|
||||
inline: 2,
|
||||
},
|
||||
mangle: {
|
||||
safari10: true,
|
||||
keep_classnames: true,
|
||||
keep_fnames: true,
|
||||
module: true
|
||||
},
|
||||
// Added for profiling in devtools
|
||||
keep_classnames: isEnvProductionProfile,
|
||||
keep_fnames: isEnvProductionProfile,
|
||||
output: {
|
||||
ecma: 5,
|
||||
comments: false,
|
||||
// Turned on because emoji and regex is not minified properly using default
|
||||
// https://github.com/facebook/create-react-app/issues/2488
|
||||
ascii_only: true,
|
||||
},
|
||||
},
|
||||
cache: true,
|
||||
sourceMap: shouldUseSourceMap,
|
||||
}),
|
||||
// This is only used in production mode
|
||||
new webpack.BannerPlugin({
|
||||
banner: banner,
|
||||
entryOnly: true
|
||||
}),
|
||||
new OptimizeCSSAssetsPlugin({
|
||||
cssProcessorOptions: {
|
||||
parser: safePostCssParser,
|
||||
map: shouldUseSourceMap
|
||||
? {
|
||||
// `inline: false` forces the sourcemap to be output into a
|
||||
// separate file
|
||||
inline: false,
|
||||
// `annotation: true` appends the sourceMappingURL to the end of
|
||||
// the css file, helping the browser find the sourcemap
|
||||
annotation: true,
|
||||
}
|
||||
: false,
|
||||
},
|
||||
cssProcessorPluginOptions: {
|
||||
preset: ['default', { minifyFontValues: { removeQuotes: false } }],
|
||||
},
|
||||
}),
|
||||
],
|
||||
// Automatically split vendor and commons
|
||||
// https://twitter.com/wSokra/status/969633336732905474
|
||||
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
|
||||
splitChunks: {
|
||||
cacheGroups: {
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
// Keep the runtime chunk separated to enable long term caching
|
||||
// https://twitter.com/wSokra/status/969679223278505985
|
||||
// https://github.com/facebook/create-react-app/issues/5358
|
||||
runtimeChunk: false,
|
||||
},
|
||||
resolve: {
|
||||
// This allows you to set a fallback for where webpack should look for modules.
|
||||
// We placed these paths second because we want `node_modules` to "win"
|
||||
// if there are any conflicts. This matches Node resolution mechanism.
|
||||
// https://github.com/facebook/create-react-app/issues/253
|
||||
modules: ['node_modules', paths.appNodeModules].concat(
|
||||
modules.additionalModulePaths || []
|
||||
),
|
||||
// These are the reasonable defaults supported by the Node ecosystem.
|
||||
// We also include JSX as a common component filename extension to support
|
||||
// some tools, although we do not recommend using it, see:
|
||||
// https://github.com/facebook/create-react-app/issues/290
|
||||
// `web` extension prefixes have been added for better support
|
||||
// for React Native Web.
|
||||
extensions: paths.moduleFileExtensions
|
||||
.map(ext => `.${ext}`)
|
||||
.filter(ext => useTypeScript || !ext.includes('ts')),
|
||||
alias: {
|
||||
// Support React Native Web
|
||||
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
|
||||
'react-native': 'react-native-web',
|
||||
// Allows for better profiling with ReactDevTools
|
||||
...(isEnvProductionProfile && {
|
||||
'react-dom$': 'react-dom/profiling',
|
||||
'scheduler/tracing': 'scheduler/tracing-profiling',
|
||||
}),
|
||||
...(modules.webpackAliases || {}),
|
||||
},
|
||||
plugins: [
|
||||
// Adds support for installing with Plug'n'Play, leading to faster installs and adding
|
||||
// guards against forgotten dependencies and such.
|
||||
PnpWebpackPlugin,
|
||||
// Prevents users from importing files from outside of src/ (or node_modules/).
|
||||
// This often causes confusion because we only process files within src/ with babel.
|
||||
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
|
||||
// please link the files into your node_modules/ and let module-resolution kick in.
|
||||
// Make sure your source files are compiled, as they will not be processed in any way.
|
||||
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
|
||||
],
|
||||
},
|
||||
resolveLoader: {
|
||||
plugins: [
|
||||
// Also related to Plug'n'Play, but this time it tells webpack to load its loaders
|
||||
// from the current package.
|
||||
PnpWebpackPlugin.moduleLoader(module),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
strictExportPresence: true,
|
||||
rules: [
|
||||
// Disable require.ensure as it's not a standard language feature.
|
||||
{ parser: { requireEnsure: false } },
|
||||
|
||||
// First, run the linter.
|
||||
// It's important to do this before Babel processes the JS.
|
||||
{
|
||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
enforce: 'pre',
|
||||
use: [
|
||||
{
|
||||
options: {
|
||||
cache: true,
|
||||
formatter: require.resolve('react-dev-utils/eslintFormatter'),
|
||||
eslintPath: require.resolve('eslint'),
|
||||
resolvePluginsRelativeTo: __dirname,
|
||||
|
||||
},
|
||||
loader: require.resolve('eslint-loader'),
|
||||
},
|
||||
],
|
||||
include: paths.appSrcLib,
|
||||
},
|
||||
{
|
||||
// "oneOf" will traverse all following loaders until one will
|
||||
// match the requirements. When no loader matches it will fall
|
||||
// back to the "file" loader at the end of the loader list.
|
||||
oneOf: [
|
||||
// "url" loader works like "file" loader except that it embeds assets
|
||||
// smaller than specified limit in bytes as data URLs to avoid requests.
|
||||
// A missing `test` is equivalent to a match.
|
||||
{
|
||||
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
|
||||
loader: require.resolve('url-loader'),
|
||||
options: {
|
||||
limit: imageInlineSizeLimit,
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// Process application JS with Babel.
|
||||
// The preset includes JSX, Flow, TypeScript, and some ESnext features.
|
||||
{
|
||||
test: /\.(js|mjs|jsx|ts|tsx)$/,
|
||||
include: paths.appSrcLib,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
customize: require.resolve(
|
||||
'babel-preset-react-app/webpack-overrides'
|
||||
),
|
||||
|
||||
plugins: [
|
||||
[
|
||||
require.resolve('babel-plugin-named-asset-import'),
|
||||
{
|
||||
loaderMap: {
|
||||
svg: {
|
||||
ReactComponent:
|
||||
'@svgr/webpack?-svgo,+titleProp,+ref![path]',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
// This is a feature of `babel-loader` for webpack (not Babel itself).
|
||||
// It enables caching results in ./node_modules/.cache/babel-loader/
|
||||
// directory for faster rebuilds.
|
||||
cacheDirectory: true,
|
||||
// See #6846 for context on why cacheCompression is disabled
|
||||
cacheCompression: false,
|
||||
compact: isEnvProduction,
|
||||
},
|
||||
},
|
||||
// Process any JS outside of the app with Babel.
|
||||
// Unlike the application JS, we only compile the standard ES features.
|
||||
{
|
||||
test: /\.(js|mjs)$/,
|
||||
exclude: /@babel(?:\/|\\{1,2})runtime/,
|
||||
loader: require.resolve('babel-loader'),
|
||||
options: {
|
||||
babelrc: false,
|
||||
configFile: false,
|
||||
compact: false,
|
||||
presets: [
|
||||
[
|
||||
require.resolve('babel-preset-react-app/dependencies'),
|
||||
{ helpers: true },
|
||||
],
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react"
|
||||
],
|
||||
plugins: [
|
||||
[
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
]
|
||||
],
|
||||
cacheDirectory: true,
|
||||
// See #6846 for context on why cacheCompression is disabled
|
||||
cacheCompression: false,
|
||||
|
||||
// Babel sourcemaps are needed for debugging into node_modules
|
||||
// code. Without the options below, debuggers like VSCode
|
||||
// show incorrect code and set breakpoints on the wrong lines.
|
||||
sourceMaps: shouldUseSourceMap,
|
||||
inputSourceMap: shouldUseSourceMap,
|
||||
},
|
||||
},
|
||||
// "postcss" loader applies autoprefixer to our CSS.
|
||||
// "css" loader resolves paths in CSS and adds assets as dependencies.
|
||||
// "style" loader turns CSS into JS modules that inject <style> tags.
|
||||
// In production, we use MiniCSSExtractPlugin to extract that CSS
|
||||
// to a file, but in development "style" loader enables hot editing
|
||||
// of CSS.
|
||||
// By default we support CSS Modules with the extension .module.css
|
||||
{
|
||||
test: cssRegex,
|
||||
exclude: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
}),
|
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true,
|
||||
},
|
||||
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
|
||||
// using the extension .module.css
|
||||
{
|
||||
test: cssModuleRegex,
|
||||
use: getStyleLoaders({
|
||||
importLoaders: 1,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
}),
|
||||
},
|
||||
// Opt-in support for SASS (using .scss or .sass extensions).
|
||||
// By default we support SASS Modules with the
|
||||
// extensions .module.scss or .module.sass
|
||||
{
|
||||
test: sassRegex,
|
||||
exclude: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
// Don't consider CSS imports dead code even if the
|
||||
// containing package claims to have no side effects.
|
||||
// Remove this when webpack adds a warning or an error for this.
|
||||
// See https://github.com/webpack/webpack/issues/6571
|
||||
sideEffects: true,
|
||||
},
|
||||
// Adds support for CSS Modules, but using SASS
|
||||
// using the extension .module.scss or .module.sass
|
||||
{
|
||||
test: sassModuleRegex,
|
||||
use: getStyleLoaders(
|
||||
{
|
||||
importLoaders: 3,
|
||||
sourceMap: isEnvProduction && shouldUseSourceMap,
|
||||
modules: {
|
||||
getLocalIdent: getCSSModuleLocalIdent,
|
||||
},
|
||||
},
|
||||
'sass-loader'
|
||||
),
|
||||
},
|
||||
// "file" loader makes sure those assets get served by WebpackDevServer.
|
||||
// When you `import` an asset, you get its (virtual) filename.
|
||||
// In production, they would get copied to the `build` folder.
|
||||
// This loader doesn't use a "test" so it will catch all modules
|
||||
// that fall through the other loaders.
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
// Exclude `js` files to keep "css" loader working as it injects
|
||||
// its runtime that would otherwise be processed through "file" loader.
|
||||
// Also exclude `html` and `json` extensions so they get processed
|
||||
// by webpacks internal loaders.
|
||||
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
|
||||
options: {
|
||||
name: 'static/media/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
// ** STOP ** Are you adding a new loader?
|
||||
// Make sure to add the new loader(s) before the "file" loader.
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
isEnvDevelopment &&
|
||||
// Generates an `index.html` file with the <script> injected.
|
||||
new HtmlWebpackPlugin(
|
||||
Object.assign(
|
||||
{},
|
||||
{
|
||||
inject: true,
|
||||
template: paths.appHtml,
|
||||
},
|
||||
isEnvProduction
|
||||
? {
|
||||
minify: {
|
||||
removeComments: true,
|
||||
collapseWhitespace: true,
|
||||
removeRedundantAttributes: true,
|
||||
useShortDoctype: true,
|
||||
removeEmptyAttributes: true,
|
||||
removeStyleLinkTypeAttributes: true,
|
||||
keepClosingSlash: true,
|
||||
minifyJS: true,
|
||||
minifyCSS: true,
|
||||
minifyURLs: true,
|
||||
},
|
||||
}
|
||||
: undefined
|
||||
)
|
||||
),
|
||||
// Inlines the webpack runtime script. This script is too small to warrant
|
||||
// a network request.
|
||||
// https://github.com/facebook/create-react-app/issues/5358
|
||||
isEnvProduction &&
|
||||
shouldInlineRuntimeChunk &&
|
||||
new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
|
||||
// Makes some environment variables available in index.html.
|
||||
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
|
||||
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// It will be an empty string unless you specify "homepage"
|
||||
// in `package.json`, in which case it will be the pathname of that URL.
|
||||
new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
|
||||
// This gives some necessary context to module not found errors, such as
|
||||
// the requesting resource.
|
||||
new ModuleNotFoundPlugin(paths.appPath),
|
||||
// Makes some environment variables available to the JS code, for example:
|
||||
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
|
||||
// It is absolutely essential that NODE_ENV is set to production
|
||||
// during a production build.
|
||||
// Otherwise React will be compiled in the very slow development mode.
|
||||
new webpack.DefinePlugin(env.stringified),
|
||||
// This is necessary to emit hot updates (currently CSS only):
|
||||
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
|
||||
// Watcher doesn't work well if you mistype casing in a path so we use
|
||||
// a plugin that prints an error when you attempt to do this.
|
||||
// See https://github.com/facebook/create-react-app/issues/240
|
||||
isEnvDevelopment && new CaseSensitivePathsPlugin(),
|
||||
// If you require a missing module and then `npm install` it, you still have
|
||||
// to restart the development server for webpack to discover it. This plugin
|
||||
// makes the discovery automatic so you don't have to restart.
|
||||
// See https://github.com/facebook/create-react-app/issues/186
|
||||
isEnvDevelopment &&
|
||||
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
|
||||
isEnvProduction &&
|
||||
new MiniCssExtractPlugin({
|
||||
// Options similar to the same options in webpackOptions.output
|
||||
// both options are optional
|
||||
filename: 'css/index.css',
|
||||
chunkFilename: 'css/index.[contenthash:8].chunk.css',
|
||||
}),
|
||||
// Generate an asset manifest file with the following content:
|
||||
// - "files" key: Mapping of all asset filenames to their corresponding
|
||||
// output file so that tools can pick it up without having to parse
|
||||
// `index.html`
|
||||
// - "entrypoints" key: Array of files which are included in `index.html`,
|
||||
// can be used to reconstruct the HTML if necessary
|
||||
/*new ManifestPlugin({
|
||||
fileName: 'asset-manifest.json',
|
||||
publicPath: paths.publicUrlOrPath,
|
||||
generate: (seed, files, entrypoints) => {
|
||||
const manifestFiles = files.reduce((manifest, file) => {
|
||||
manifest[file.name] = file.path;
|
||||
return manifest;
|
||||
}, seed);
|
||||
const entrypointFiles = entrypoints.main.filter(
|
||||
fileName => !fileName.endsWith('.map')
|
||||
);
|
||||
|
||||
return {
|
||||
files: manifestFiles,
|
||||
entrypoints: entrypointFiles,
|
||||
};
|
||||
},
|
||||
}),*/
|
||||
// Moment.js is an extremely popular library that bundles large locale files
|
||||
// by default due to how webpack interprets its code. This is a practical
|
||||
// solution that requires the user to opt into importing specific locales.
|
||||
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
|
||||
// You can remove this if you don't use Moment.js:
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
|
||||
new CopyWebpackPlugin([
|
||||
{
|
||||
from: `${paths.appSrcLibTypes}`,
|
||||
to: paths.appBuild
|
||||
}
|
||||
]),
|
||||
new PrettierPlugin(),
|
||||
// Generate a service worker script that will precache, and keep up to date,
|
||||
// the HTML & assets that are part of the webpack build.
|
||||
/*isEnvProduction &&
|
||||
new WorkboxWebpackPlugin.GenerateSW({
|
||||
clientsClaim: true,
|
||||
exclude: [/\.map$/, /asset-manifest\.json$/],
|
||||
importWorkboxFrom: 'cdn',
|
||||
navigateFallback: paths.publicUrlOrPath + 'index.html',
|
||||
navigateFallbackBlacklist: [
|
||||
// Exclude URLs starting with /_, as they're likely an API call
|
||||
new RegExp('^/_'),
|
||||
// Exclude any URLs whose last part seems to be a file extension
|
||||
// as they're likely a resource and not a SPA route.
|
||||
// URLs containing a "?" character won't be blacklisted as they're likely
|
||||
// a route with query params (e.g. auth callbacks).
|
||||
new RegExp('/[^/?]+\\.[^/]+$'),
|
||||
],
|
||||
}),*/
|
||||
// TypeScript type checking
|
||||
useTypeScript &&
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: resolve.sync('typescript', {
|
||||
basedir: paths.appNodeModules,
|
||||
}),
|
||||
async: isEnvDevelopment,
|
||||
useTypescriptIncrementalApi: true,
|
||||
checkSyntacticErrors: true,
|
||||
resolveModuleNameModule: process.versions.pnp
|
||||
? `${__dirname}/pnpTs.js`
|
||||
: undefined,
|
||||
resolveTypeReferenceDirectiveModule: process.versions.pnp
|
||||
? `${__dirname}/pnpTs.js`
|
||||
: undefined,
|
||||
tsconfig: paths.appTsConfig,
|
||||
reportFiles: [
|
||||
'**',
|
||||
'!**/__tests__/**',
|
||||
'!**/?(*.)(spec|test).*',
|
||||
'!**/src/setupProxy.*',
|
||||
'!**/src/setupTests.*',
|
||||
],
|
||||
silent: true,
|
||||
// The formatter is invoked directly in WebpackDevServerUtils during development
|
||||
formatter: isEnvProduction ? typescriptFormatter : undefined,
|
||||
}),
|
||||
].filter(Boolean),
|
||||
// Some libraries import Node modules but don't use them in the browser.
|
||||
// Tell webpack to provide empty mocks for them so importing them works.
|
||||
node: {
|
||||
module: 'empty',
|
||||
dgram: 'empty',
|
||||
dns: 'mock',
|
||||
fs: 'empty',
|
||||
http2: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty',
|
||||
},
|
||||
// Turn off performance processing because we utilize
|
||||
// our own hints via the FileSizeReporter
|
||||
performance: false,
|
||||
};
|
||||
};
|
@ -1,130 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
const fs = require('fs');
|
||||
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
|
||||
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
|
||||
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
|
||||
const ignoredFiles = require('react-dev-utils/ignoredFiles');
|
||||
const redirectServedPath = require('react-dev-utils/redirectServedPathMiddleware');
|
||||
const paths = require('./paths');
|
||||
const getHttpsConfig = require('./getHttpsConfig');
|
||||
|
||||
const host = process.env.HOST || '0.0.0.0';
|
||||
const sockHost = process.env.WDS_SOCKET_HOST;
|
||||
const sockPath = process.env.WDS_SOCKET_PATH; // default: '/sockjs-node'
|
||||
const sockPort = process.env.WDS_SOCKET_PORT;
|
||||
|
||||
module.exports = function(proxy, allowedHost) {
|
||||
return {
|
||||
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
|
||||
// websites from potentially accessing local content through DNS rebinding:
|
||||
// https://github.com/webpack/webpack-dev-server/issues/887
|
||||
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
|
||||
// However, it made several existing use cases such as development in cloud
|
||||
// environment or subdomains in development significantly more complicated:
|
||||
// https://github.com/facebook/create-react-app/issues/2271
|
||||
// https://github.com/facebook/create-react-app/issues/2233
|
||||
// While we're investigating better solutions, for now we will take a
|
||||
// compromise. Since our WDS configuration only serves files in the `public`
|
||||
// folder we won't consider accessing them a vulnerability. However, if you
|
||||
// use the `proxy` feature, it gets more dangerous because it can expose
|
||||
// remote code execution vulnerabilities in backends like Django and Rails.
|
||||
// So we will disable the host check normally, but enable it if you have
|
||||
// specified the `proxy` setting. Finally, we let you override it if you
|
||||
// really know what you're doing with a special environment variable.
|
||||
disableHostCheck:
|
||||
!proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true',
|
||||
// Enable gzip compression of generated files.
|
||||
compress: true,
|
||||
// Silence WebpackDevServer's own logs since they're generally not useful.
|
||||
// It will still show compile warnings and errors with this setting.
|
||||
clientLogLevel: 'none',
|
||||
// By default WebpackDevServer serves physical files from current directory
|
||||
// in addition to all the virtual build products that it serves from memory.
|
||||
// This is confusing because those files won’t automatically be available in
|
||||
// production build folder unless we copy them. However, copying the whole
|
||||
// project directory is dangerous because we may expose sensitive files.
|
||||
// Instead, we establish a convention that only files in `public` directory
|
||||
// get served. Our build script will copy `public` into the `build` folder.
|
||||
// In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
|
||||
// <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
// In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
|
||||
// Note that we only recommend to use `public` folder as an escape hatch
|
||||
// for files like `favicon.ico`, `manifest.json`, and libraries that are
|
||||
// for some reason broken when imported through webpack. If you just want to
|
||||
// use an image, put it in `src` and `import` it from JavaScript instead.
|
||||
contentBase: paths.appPublic,
|
||||
contentBasePublicPath: paths.publicUrlOrPath,
|
||||
// By default files from `contentBase` will not trigger a page reload.
|
||||
watchContentBase: true,
|
||||
// Enable hot reloading server. It will provide WDS_SOCKET_PATH endpoint
|
||||
// for the WebpackDevServer client so it can learn when the files were
|
||||
// updated. The WebpackDevServer client is included as an entry point
|
||||
// in the webpack development configuration. Note that only changes
|
||||
// to CSS are currently hot reloaded. JS changes will refresh the browser.
|
||||
hot: true,
|
||||
// Use 'ws' instead of 'sockjs-node' on server since we're using native
|
||||
// websockets in `webpackHotDevClient`.
|
||||
transportMode: 'ws',
|
||||
// Prevent a WS client from getting injected as we're already including
|
||||
// `webpackHotDevClient`.
|
||||
injectClient: false,
|
||||
// Enable custom sockjs pathname for websocket connection to hot reloading server.
|
||||
// Enable custom sockjs hostname, pathname and port for websocket connection
|
||||
// to hot reloading server.
|
||||
sockHost,
|
||||
sockPath,
|
||||
sockPort,
|
||||
// It is important to tell WebpackDevServer to use the same "publicPath" path as
|
||||
// we specified in the webpack config. When homepage is '.', default to serving
|
||||
// from the root.
|
||||
// remove last slash so user can land on `/test` instead of `/test/`
|
||||
publicPath: paths.publicUrlOrPath.slice(0, -1),
|
||||
// WebpackDevServer is noisy by default so we emit custom message instead
|
||||
// by listening to the compiler events with `compiler.hooks[...].tap` calls above.
|
||||
quiet: true,
|
||||
// Reportedly, this avoids CPU overload on some systems.
|
||||
// https://github.com/facebook/create-react-app/issues/293
|
||||
// src/node_modules is not ignored to support absolute imports
|
||||
// https://github.com/facebook/create-react-app/issues/1065
|
||||
watchOptions: {
|
||||
ignored: ignoredFiles(paths.appSrc),
|
||||
},
|
||||
https: getHttpsConfig(),
|
||||
host,
|
||||
overlay: false,
|
||||
historyApiFallback: {
|
||||
// Paths with dots should still use the history fallback.
|
||||
// See https://github.com/facebook/create-react-app/issues/387.
|
||||
disableDotRule: true,
|
||||
index: paths.publicUrlOrPath,
|
||||
},
|
||||
public: allowedHost,
|
||||
// `proxy` is run between `before` and `after` `webpack-dev-server` hooks
|
||||
proxy,
|
||||
before(app, server) {
|
||||
// Keep `evalSourceMapMiddleware` and `errorOverlayMiddleware`
|
||||
// middlewares before `redirectServedPath` otherwise will not have any effect
|
||||
// This lets us fetch source contents from webpack for the error overlay
|
||||
app.use(evalSourceMapMiddleware(server));
|
||||
// This lets us open files from the runtime error overlay.
|
||||
app.use(errorOverlayMiddleware());
|
||||
|
||||
if (fs.existsSync(paths.proxySetup)) {
|
||||
// This registers user provided middleware for proxy reasons
|
||||
require(paths.proxySetup)(app);
|
||||
}
|
||||
},
|
||||
after(app) {
|
||||
// Redirect to `PUBLIC_URL` or `homepage` from `package.json` if url not match
|
||||
app.use(redirectServedPath(paths.publicUrlOrPath));
|
||||
|
||||
// This service worker file is effectively a 'no-op' that will reset any
|
||||
// previous service worker registered for the same host:port combination.
|
||||
// We do this in development to avoid hitting the production cache if
|
||||
// it used the same host and port.
|
||||
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
|
||||
app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath));
|
||||
},
|
||||
};
|
||||
};
|
12
demo/index.html
Normal file
12
demo/index.html
Normal file
@ -0,0 +1,12 @@
|
||||
<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>
|
12
demo/main.css
Normal file
12
demo/main.css
Normal file
@ -0,0 +1,12 @@
|
||||
input {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
padding: 20px;
|
||||
font-size: 20px;
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.simple-keyboard {
|
||||
max-width: 850px;
|
||||
}
|
38
demo/main.js
Normal file
38
demo/main.js
Normal file
@ -0,0 +1,38 @@
|
||||
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
|
||||
});
|
||||
}
|
19181
package-lock.json
generated
19181
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
139
package.json
139
package.json
@ -1,15 +1,14 @@
|
||||
{
|
||||
"name": "simple-keyboard",
|
||||
"version": "2.32.119",
|
||||
"version": "3.0.0-beta.0",
|
||||
"description": "On-screen Javascript Virtual Keyboard",
|
||||
"main": "build/index.js",
|
||||
"types": "build/index.d.ts",
|
||||
"scripts": {
|
||||
"start": "node scripts/start.js",
|
||||
"build": "node scripts/build.js",
|
||||
"demo": "node scripts/demo.js",
|
||||
"test": "node scripts/test.js",
|
||||
"coverage": "node scripts/test.js --coverage --watchAll=false",
|
||||
"start": "webpack serve",
|
||||
"build": "webpack && tsc",
|
||||
"test": "jest --silent",
|
||||
"coverage": "npm run test -- --coverage",
|
||||
"prepare": "npm run build",
|
||||
"trypublish": "npm publish || true"
|
||||
},
|
||||
@ -40,85 +39,32 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.13.8",
|
||||
"@babel/cli": "^7.13.0",
|
||||
"@babel/core": "^7.13.8",
|
||||
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||
"@babel/plugin-transform-typescript": "^7.13.0",
|
||||
"@babel/polyfill": "^7.12.1",
|
||||
"@babel/preset-env": "^7.13.9",
|
||||
"@babel/preset-react": "^7.12.13",
|
||||
"@svgr/webpack": "5.5.0",
|
||||
"@testing-library/jest-dom": "^5.11.9",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/user-event": "^12.8.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.10.0",
|
||||
"@typescript-eslint/parser": "^2.10.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.16.1",
|
||||
"@typescript-eslint/parser": "^4.16.1",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-jest": "^26.6.3",
|
||||
"babel-loader": "8.2.2",
|
||||
"babel-plugin-named-asset-import": "^0.3.7",
|
||||
"babel-preset-react-app": "^10.0.0",
|
||||
"camelcase": "^6.2.0",
|
||||
"case-sensitive-paths-webpack-plugin": "2.4.0",
|
||||
"copy-webpack-plugin": "^5.1.2",
|
||||
"css-loader": "5.1.1",
|
||||
"dotenv": "8.2.0",
|
||||
"dotenv-expand": "5.1.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-react-app": "^5.0.1",
|
||||
"eslint-loader": "4.0.2",
|
||||
"eslint-plugin-flowtype": "4.7.0",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"eslint-plugin-jsx-a11y": "6.4.1",
|
||||
"eslint-plugin-react": "7.22.0",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"file-loader": "6.2.0",
|
||||
"fs-extra": "9.1.0",
|
||||
"html-webpack-plugin": "4.5.2",
|
||||
"identity-obj-proxy": "3.0.0",
|
||||
"jest": "26.6.3",
|
||||
"jest-environment-jsdom-fourteen": "1.0.1",
|
||||
"jest-resolve": "26.6.2",
|
||||
"jest-watch-typeahead": "0.6.1",
|
||||
"mini-css-extract-plugin": "1.3.9",
|
||||
"optimize-css-assets-webpack-plugin": "5.0.4",
|
||||
"pnp-webpack-plugin": "1.6.4",
|
||||
"postcss-flexbugs-fixes": "4.2.1",
|
||||
"postcss-loader": "3.0.0",
|
||||
"postcss-normalize": "9.0.0",
|
||||
"postcss-preset-env": "6.7.0",
|
||||
"postcss-safe-parser": "5.0.2",
|
||||
"prettier": "^1.19.1",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-preset-minify": "^0.5.0",
|
||||
"css-loader": "^5.1.1",
|
||||
"eslint": "^7.21.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"jest": "^26.6.3",
|
||||
"mini-css-extract-plugin": "^1.3.9",
|
||||
"optimize-css-assets-webpack-plugin": "^5.0.4",
|
||||
"prettier": "^2.2.1",
|
||||
"prettier-webpack-plugin": "^1.2.0",
|
||||
"react": "^17.0.1",
|
||||
"react-app-polyfill": "^2.0.0",
|
||||
"react-dev-utils": "^11.0.3",
|
||||
"react-dom": "^17.0.1",
|
||||
"resolve": "1.20.0",
|
||||
"resolve-url-loader": "3.1.2",
|
||||
"sass-loader": "10.1.1",
|
||||
"semver": "7.3.4",
|
||||
"style-loader": "2.0.0",
|
||||
"terser-webpack-plugin": "4.2.3",
|
||||
"ts-pnp": "1.2.0",
|
||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||
"url-loader": "4.1.1",
|
||||
"webpack": "4.46.0",
|
||||
"webpack-dev-server": "3.11.2",
|
||||
"webpack-manifest-plugin": "3.0.0",
|
||||
"workbox-webpack-plugin": "6.1.1"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
"style-loader": "^2.0.0",
|
||||
"terser-webpack-plugin": "^5.1.1",
|
||||
"typescript": "^4.2.3",
|
||||
"url-loader": "^4.1.1",
|
||||
"webpack": "^5.24.3",
|
||||
"webpack-cli": "^4.5.0",
|
||||
"webpack-dev-server": "4.0.0-beta.0"
|
||||
},
|
||||
"jest": {
|
||||
"roots": [
|
||||
@ -133,30 +79,18 @@
|
||||
"!src/**/*.d.ts",
|
||||
"!**/tests/**"
|
||||
],
|
||||
"setupFiles": [
|
||||
"react-app-polyfill/jsdom"
|
||||
],
|
||||
"setupFilesAfterEnv": [
|
||||
"<rootDir>/src/setupTests.js"
|
||||
],
|
||||
"testMatch": [
|
||||
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
|
||||
"<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
|
||||
],
|
||||
"testEnvironment": "jest-environment-jsdom-fourteen",
|
||||
"transform": {
|
||||
"^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
|
||||
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
|
||||
"^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
|
||||
},
|
||||
"transformIgnorePatterns": [
|
||||
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
|
||||
"^.+\\.module\\.(css|sass|scss)$"
|
||||
],
|
||||
"modulePaths": [],
|
||||
"moduleNameMapper": {
|
||||
"^react-native$": "react-native-web",
|
||||
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
|
||||
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/scripts/testMock.js",
|
||||
"\\.(css|less)$": "<rootDir>/scripts/testMock.js"
|
||||
},
|
||||
"moduleFileExtensions": [
|
||||
"web.js",
|
||||
@ -169,21 +103,6 @@
|
||||
"web.jsx",
|
||||
"jsx",
|
||||
"node"
|
||||
],
|
||||
"watchPlugins": [
|
||||
"jest-watch-typeahead/filename",
|
||||
"jest-watch-typeahead/testname"
|
||||
]
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": [
|
||||
[
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.1 KiB |
@ -1,28 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<title>simple-keyboard</title>
|
||||
<style>
|
||||
/**
|
||||
* Disabling double-tap to zoom in iOS 10+
|
||||
* as it interferes with simple-keyboard
|
||||
*/
|
||||
body,
|
||||
html {
|
||||
touch-action: manipulation;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
209
scripts/build.js
209
scripts/build.js
@ -1,209 +0,0 @@
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'production';
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
|
||||
const path = require('path');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const fs = require('fs-extra');
|
||||
const webpack = require('webpack');
|
||||
const configFactory = require('../config/webpack.config');
|
||||
const paths = require('../config/paths');
|
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
|
||||
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
|
||||
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
|
||||
const printBuildError = require('react-dev-utils/printBuildError');
|
||||
|
||||
const measureFileSizesBeforeBuild =
|
||||
FileSizeReporter.measureFileSizesBeforeBuild;
|
||||
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
|
||||
// These sizes are pretty large. We'll warn for bundles exceeding them.
|
||||
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
|
||||
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
|
||||
|
||||
const isInteractive = process.stdout.isTTY;
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Generate configuration
|
||||
const config = configFactory('production');
|
||||
|
||||
// We require that you explicitly set browsers and do not fall back to
|
||||
// browserslist defaults.
|
||||
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
|
||||
checkBrowsers(paths.appPath, isInteractive)
|
||||
.then(() => {
|
||||
// First, read the current file sizes in build directory.
|
||||
// This lets us display how much they changed later.
|
||||
return measureFileSizesBeforeBuild(paths.appBuild);
|
||||
})
|
||||
.then(previousFileSizes => {
|
||||
// Remove all content but keep the directory so that
|
||||
// if you're in it, you don't end up in Trash
|
||||
fs.emptyDirSync(paths.appBuild);
|
||||
// Merge with the public folder
|
||||
//copyPublicFolder();
|
||||
// Start the webpack build
|
||||
return build(previousFileSizes);
|
||||
})
|
||||
.then(
|
||||
({ stats, previousFileSizes, warnings }) => {
|
||||
if (warnings.length) {
|
||||
console.log(chalk.yellow('Compiled with warnings.\n'));
|
||||
console.log(warnings.join('\n\n'));
|
||||
console.log(
|
||||
'\nSearch for the ' +
|
||||
chalk.underline(chalk.yellow('keywords')) +
|
||||
' to learn more about each warning.'
|
||||
);
|
||||
console.log(
|
||||
'To ignore, add ' +
|
||||
chalk.cyan('// eslint-disable-next-line') +
|
||||
' to the line before.\n'
|
||||
);
|
||||
} else {
|
||||
console.log(chalk.green('Compiled successfully.\n'));
|
||||
}
|
||||
|
||||
console.log('File sizes after gzip:\n');
|
||||
printFileSizesAfterBuild(
|
||||
stats,
|
||||
previousFileSizes,
|
||||
paths.appBuild,
|
||||
WARN_AFTER_BUNDLE_GZIP_SIZE,
|
||||
WARN_AFTER_CHUNK_GZIP_SIZE
|
||||
);
|
||||
console.log();
|
||||
|
||||
const appPackage = require(paths.appPackageJson);
|
||||
const publicUrl = paths.publicUrlOrPath;
|
||||
const publicPath = config.output.publicPath;
|
||||
const buildFolder = path.relative(process.cwd(), paths.appBuild);
|
||||
printHostingInstructions(
|
||||
appPackage,
|
||||
publicUrl,
|
||||
publicPath,
|
||||
buildFolder,
|
||||
useYarn
|
||||
);
|
||||
},
|
||||
err => {
|
||||
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
|
||||
if (tscCompileOnError) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Compiled with the following type errors (you may want to check these before deploying your app):\n'
|
||||
)
|
||||
);
|
||||
printBuildError(err);
|
||||
} else {
|
||||
console.log(chalk.red('Failed to compile.\n'));
|
||||
printBuildError(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch(err => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Create the production build and print the deployment instructions.
|
||||
function build(previousFileSizes) {
|
||||
// We used to support resolving modules according to `NODE_PATH`.
|
||||
// This now has been deprecated in favor of jsconfig/tsconfig.json
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
if (process.env.NODE_PATH) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
|
||||
)
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log('Creating an optimized production build...');
|
||||
|
||||
const compiler = webpack(config);
|
||||
return new Promise((resolve, reject) => {
|
||||
compiler.run((err, stats) => {
|
||||
let messages;
|
||||
if (err) {
|
||||
if (!err.message) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
let errMessage = err.message;
|
||||
|
||||
// Add additional information for postcss errors
|
||||
if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
|
||||
errMessage +=
|
||||
'\nCompileError: Begins at CSS selector ' +
|
||||
err['postcssNode'].selector;
|
||||
}
|
||||
|
||||
messages = formatWebpackMessages({
|
||||
errors: [errMessage],
|
||||
warnings: [],
|
||||
});
|
||||
} else {
|
||||
messages = formatWebpackMessages(
|
||||
stats.toJson({ all: false, warnings: true, errors: true })
|
||||
);
|
||||
}
|
||||
if (messages.errors.length) {
|
||||
// Only keep the first error. Others are often indicative
|
||||
// of the same problem, but confuse the reader with noise.
|
||||
if (messages.errors.length > 1) {
|
||||
messages.errors.length = 1;
|
||||
}
|
||||
return reject(new Error(messages.errors.join('\n\n')));
|
||||
}
|
||||
if (
|
||||
process.env.CI &&
|
||||
(typeof process.env.CI !== 'string' ||
|
||||
process.env.CI.toLowerCase() !== 'false') &&
|
||||
messages.warnings.length
|
||||
) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'\nTreating warnings as errors because process.env.CI = true.\n' +
|
||||
'Most CI servers set it automatically.\n'
|
||||
)
|
||||
);
|
||||
return reject(new Error(messages.warnings.join('\n\n')));
|
||||
}
|
||||
|
||||
return resolve({
|
||||
stats,
|
||||
previousFileSizes,
|
||||
warnings: messages.warnings,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function copyPublicFolder() {
|
||||
fs.copySync(paths.appPublic, paths.appBuild, {
|
||||
dereference: true,
|
||||
filter: file => file !== paths.appHtml,
|
||||
});
|
||||
}
|
209
scripts/demo.js
209
scripts/demo.js
@ -1,209 +0,0 @@
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'production';
|
||||
process.env.NODE_ENV = 'production';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
|
||||
const path = require('path');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const fs = require('fs-extra');
|
||||
const webpack = require('webpack');
|
||||
const configFactory = require('../config/webpack.config.demo');
|
||||
const paths = require('../config/paths');
|
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
|
||||
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
|
||||
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
|
||||
const printBuildError = require('react-dev-utils/printBuildError');
|
||||
|
||||
const measureFileSizesBeforeBuild =
|
||||
FileSizeReporter.measureFileSizesBeforeBuild;
|
||||
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
|
||||
// These sizes are pretty large. We'll warn for bundles exceeding them.
|
||||
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
|
||||
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
|
||||
|
||||
const isInteractive = process.stdout.isTTY;
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appDemoIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Generate configuration
|
||||
const config = configFactory('production');
|
||||
|
||||
// We require that you explicitly set browsers and do not fall back to
|
||||
// browserslist defaults.
|
||||
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
|
||||
checkBrowsers(paths.appPath, isInteractive)
|
||||
.then(() => {
|
||||
// First, read the current file sizes in build directory.
|
||||
// This lets us display how much they changed later.
|
||||
return measureFileSizesBeforeBuild(paths.appDemo);
|
||||
})
|
||||
.then(previousFileSizes => {
|
||||
// Remove all content but keep the directory so that
|
||||
// if you're in it, you don't end up in Trash
|
||||
fs.emptyDirSync(paths.appDemo);
|
||||
// Merge with the public folder
|
||||
copyPublicFolder();
|
||||
// Start the webpack build
|
||||
return build(previousFileSizes);
|
||||
})
|
||||
.then(
|
||||
({ stats, previousFileSizes, warnings }) => {
|
||||
if (warnings.length) {
|
||||
console.log(chalk.yellow('Compiled with warnings.\n'));
|
||||
console.log(warnings.join('\n\n'));
|
||||
console.log(
|
||||
'\nSearch for the ' +
|
||||
chalk.underline(chalk.yellow('keywords')) +
|
||||
' to learn more about each warning.'
|
||||
);
|
||||
console.log(
|
||||
'To ignore, add ' +
|
||||
chalk.cyan('// eslint-disable-next-line') +
|
||||
' to the line before.\n'
|
||||
);
|
||||
} else {
|
||||
console.log(chalk.green('Compiled successfully.\n'));
|
||||
}
|
||||
|
||||
console.log('File sizes after gzip:\n');
|
||||
printFileSizesAfterBuild(
|
||||
stats,
|
||||
previousFileSizes,
|
||||
paths.appDemo,
|
||||
WARN_AFTER_BUNDLE_GZIP_SIZE,
|
||||
WARN_AFTER_CHUNK_GZIP_SIZE
|
||||
);
|
||||
console.log();
|
||||
|
||||
const appPackage = require(paths.appPackageJson);
|
||||
const publicUrl = paths.publicUrlOrPath;
|
||||
const publicPath = config.output.publicPath;
|
||||
const buildFolder = path.relative(process.cwd(), paths.appDemo);
|
||||
printHostingInstructions(
|
||||
appPackage,
|
||||
publicUrl,
|
||||
publicPath,
|
||||
buildFolder,
|
||||
useYarn
|
||||
);
|
||||
},
|
||||
err => {
|
||||
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
|
||||
if (tscCompileOnError) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Compiled with the following type errors (you may want to check these before deploying your app):\n'
|
||||
)
|
||||
);
|
||||
printBuildError(err);
|
||||
} else {
|
||||
console.log(chalk.red('Failed to compile.\n'));
|
||||
printBuildError(err);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch(err => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
// Create the production build and print the deployment instructions.
|
||||
function build(previousFileSizes) {
|
||||
// We used to support resolving modules according to `NODE_PATH`.
|
||||
// This now has been deprecated in favor of jsconfig/tsconfig.json
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
if (process.env.NODE_PATH) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
|
||||
)
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log('Creating an optimized production build...');
|
||||
|
||||
const compiler = webpack(config);
|
||||
return new Promise((resolve, reject) => {
|
||||
compiler.run((err, stats) => {
|
||||
let messages;
|
||||
if (err) {
|
||||
if (!err.message) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
let errMessage = err.message;
|
||||
|
||||
// Add additional information for postcss errors
|
||||
if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
|
||||
errMessage +=
|
||||
'\nCompileError: Begins at CSS selector ' +
|
||||
err['postcssNode'].selector;
|
||||
}
|
||||
|
||||
messages = formatWebpackMessages({
|
||||
errors: [errMessage],
|
||||
warnings: [],
|
||||
});
|
||||
} else {
|
||||
messages = formatWebpackMessages(
|
||||
stats.toJson({ all: false, warnings: true, errors: true })
|
||||
);
|
||||
}
|
||||
if (messages.errors.length) {
|
||||
// Only keep the first error. Others are often indicative
|
||||
// of the same problem, but confuse the reader with noise.
|
||||
if (messages.errors.length > 1) {
|
||||
messages.errors.length = 1;
|
||||
}
|
||||
return reject(new Error(messages.errors.join('\n\n')));
|
||||
}
|
||||
if (
|
||||
process.env.CI &&
|
||||
(typeof process.env.CI !== 'string' ||
|
||||
process.env.CI.toLowerCase() !== 'false') &&
|
||||
messages.warnings.length
|
||||
) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'\nTreating warnings as errors because process.env.CI = true.\n' +
|
||||
'Most CI servers set it automatically.\n'
|
||||
)
|
||||
);
|
||||
return reject(new Error(messages.warnings.join('\n\n')));
|
||||
}
|
||||
|
||||
return resolve({
|
||||
stats,
|
||||
previousFileSizes,
|
||||
warnings: messages.warnings,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function copyPublicFolder() {
|
||||
fs.copySync(paths.appPublic, paths.appDemo, {
|
||||
dereference: true,
|
||||
filter: file => file !== paths.appHtml,
|
||||
});
|
||||
}
|
@ -22,4 +22,4 @@ const getPackageJson = function(...args) {
|
||||
}, {});
|
||||
};
|
||||
|
||||
module.exports = getPackageJson;
|
||||
module.exports = getPackageJson;
|
187
scripts/start.js
187
scripts/start.js
@ -1,187 +0,0 @@
|
||||
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'development';
|
||||
process.env.NODE_ENV = 'development';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
|
||||
const fs = require('fs');
|
||||
const chalk = require('react-dev-utils/chalk');
|
||||
const webpack = require('webpack');
|
||||
const WebpackDevServer = require('webpack-dev-server');
|
||||
const clearConsole = require('react-dev-utils/clearConsole');
|
||||
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
|
||||
const {
|
||||
choosePort,
|
||||
createCompiler,
|
||||
prepareProxy,
|
||||
prepareUrls,
|
||||
} = require('react-dev-utils/WebpackDevServerUtils');
|
||||
const openBrowser = require('react-dev-utils/openBrowser');
|
||||
const paths = require('../config/paths');
|
||||
const configFactory = require('../config/webpack.config');
|
||||
const createDevServerConfig = require('../config/webpackDevServer.config');
|
||||
|
||||
const useYarn = fs.existsSync(paths.yarnLockFile);
|
||||
const isInteractive = process.stdout.isTTY;
|
||||
|
||||
// Warn and crash if required files are missing
|
||||
if (!checkRequiredFiles([paths.appHtml, paths.appDemoIndexJs])) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Tools like Cloud9 rely on this.
|
||||
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
|
||||
const HOST = process.env.HOST || '0.0.0.0';
|
||||
|
||||
if (process.env.HOST) {
|
||||
console.log(
|
||||
chalk.cyan(
|
||||
`Attempting to bind to HOST environment variable: ${chalk.yellow(
|
||||
chalk.bold(process.env.HOST)
|
||||
)}`
|
||||
)
|
||||
);
|
||||
console.log(
|
||||
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
|
||||
);
|
||||
console.log(
|
||||
`Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
// We require that you explicitly set browsers and do not fall back to
|
||||
// browserslist defaults.
|
||||
const { checkBrowsers } = require('react-dev-utils/browsersHelper');
|
||||
checkBrowsers(paths.appPath, isInteractive)
|
||||
.then(() => {
|
||||
// We attempt to use the default port but if it is busy, we offer the user to
|
||||
// run on a different port. `choosePort()` Promise resolves to the next free port.
|
||||
return choosePort(HOST, DEFAULT_PORT);
|
||||
})
|
||||
.then(port => {
|
||||
if (port == null) {
|
||||
// We have not found a port.
|
||||
return;
|
||||
}
|
||||
const args = process.argv;
|
||||
const testMode = args[2] === "--testMode";
|
||||
const config = configFactory('development');
|
||||
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
|
||||
const appName = require(paths.appPackageJson).name;
|
||||
const useTypeScript = fs.existsSync(paths.appTsConfig);
|
||||
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
|
||||
const urls = prepareUrls(
|
||||
protocol,
|
||||
HOST,
|
||||
port,
|
||||
paths.publicUrlOrPath.slice(0, -1)
|
||||
);
|
||||
const devSocket = {
|
||||
warnings: warnings =>
|
||||
devServer.sockWrite(devServer.sockets, 'warnings', warnings),
|
||||
errors: errors =>
|
||||
devServer.sockWrite(devServer.sockets, 'errors', errors),
|
||||
};
|
||||
// Create a webpack compiler that is configured with custom messages.
|
||||
const compiler = createCompiler({
|
||||
appName,
|
||||
config,
|
||||
devSocket,
|
||||
urls,
|
||||
useYarn,
|
||||
useTypeScript,
|
||||
tscCompileOnError,
|
||||
webpack,
|
||||
});
|
||||
// Load proxy config
|
||||
const proxySetting = require(paths.appPackageJson).proxy;
|
||||
const proxyConfig = prepareProxy(
|
||||
proxySetting,
|
||||
paths.appPublic
|
||||
);
|
||||
// Serve webpack assets generated by the compiler over a web server.
|
||||
const serverConfig = createDevServerConfig(
|
||||
proxyConfig,
|
||||
urls.lanUrlForConfig
|
||||
);
|
||||
const devServer = new WebpackDevServer(compiler, serverConfig);
|
||||
// Launch WebpackDevServer.
|
||||
devServer.listen(port, HOST, err => {
|
||||
if (err) {
|
||||
return console.log(err);
|
||||
}
|
||||
if (isInteractive) {
|
||||
clearConsole();
|
||||
}
|
||||
|
||||
// We used to support resolving modules according to `NODE_PATH`.
|
||||
// This now has been deprecated in favor of jsconfig/tsconfig.json
|
||||
// This lets you use absolute paths in imports inside large monorepos:
|
||||
if (process.env.NODE_PATH) {
|
||||
console.log(
|
||||
chalk.yellow(
|
||||
'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
|
||||
)
|
||||
);
|
||||
console.log();
|
||||
}
|
||||
|
||||
console.log(chalk.cyan('Starting the development server...\n'));
|
||||
openBrowser(urls.localUrlForBrowser);
|
||||
|
||||
/**
|
||||
* Handle testMode
|
||||
*/
|
||||
if(testMode){
|
||||
compiler.hooks.done.tap('done', (stats) => {
|
||||
stats = stats.toJson();
|
||||
|
||||
if (stats.errors && stats.errors.length > 0) {
|
||||
devServer.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.warn("App started in test mode. Closing in 5 seconds.");
|
||||
const closeTimeout = setTimeout(() => {
|
||||
clearTimeout(closeTimeout);
|
||||
devServer.close();
|
||||
process.exit();
|
||||
}, 5000);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
['SIGINT', 'SIGTERM'].forEach(function(sig) {
|
||||
process.on(sig, function() {
|
||||
devServer.close();
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
|
||||
if (isInteractive || process.env.CI !== 'true') {
|
||||
// Gracefully exit when stdin ends
|
||||
process.stdin.on('end', function() {
|
||||
devServer.close();
|
||||
process.exit();
|
||||
});
|
||||
process.stdin.resume();
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
@ -1,53 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'test';
|
||||
process.env.NODE_ENV = 'test';
|
||||
process.env.PUBLIC_URL = '';
|
||||
|
||||
// Makes the script crash on unhandled rejections instead of silently
|
||||
// ignoring them. In the future, promise rejections that are not handled will
|
||||
// terminate the Node.js process with a non-zero exit code.
|
||||
process.on('unhandledRejection', err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
// Ensure environment variables are read.
|
||||
require('../config/env');
|
||||
|
||||
|
||||
const jest = require('jest');
|
||||
const execSync = require('child_process').execSync;
|
||||
let argv = process.argv.slice(2);
|
||||
|
||||
function isInGitRepository() {
|
||||
try {
|
||||
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isInMercurialRepository() {
|
||||
try {
|
||||
execSync('hg --cwd . root', { stdio: 'ignore' });
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Watch unless on CI or explicitly running all tests
|
||||
if (
|
||||
!process.env.CI &&
|
||||
argv.indexOf('--watchAll') === -1 &&
|
||||
argv.indexOf('--watchAll=false') === -1
|
||||
) {
|
||||
// https://github.com/facebook/create-react-app/issues/5210
|
||||
const hasSourceControl = isInGitRepository() || isInMercurialRepository();
|
||||
argv.push(hasSourceControl ? '--watch' : '--watchAll');
|
||||
}
|
||||
|
||||
|
||||
jest.run(argv);
|
1
scripts/testMock.js
Normal file
1
scripts/testMock.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = {};
|
329
src/lib/@types/index.d.ts
vendored
329
src/lib/@types/index.d.ts
vendored
@ -1,329 +0,0 @@
|
||||
declare module 'simple-keyboard' {
|
||||
export interface KeyboardLayoutObject {
|
||||
[key: string]: string[];
|
||||
}
|
||||
|
||||
export interface KeyboardButtonTheme {
|
||||
class: string;
|
||||
buttons: string;
|
||||
}
|
||||
|
||||
export interface KeyboardButtonAttributes {
|
||||
attribute: string;
|
||||
value: string;
|
||||
buttons: string;
|
||||
}
|
||||
|
||||
export interface KeyboardOptions {
|
||||
/**
|
||||
* Modify the keyboard layout.
|
||||
*/
|
||||
layout?: KeyboardLayoutObject;
|
||||
|
||||
/**
|
||||
* Specifies which layout should be used.
|
||||
*/
|
||||
layoutName?: string;
|
||||
|
||||
/**
|
||||
* Replaces variable buttons (such as `{bksp}`) with a human-friendly name (e.g.: `backspace`).
|
||||
*/
|
||||
display?: { [button: string]: string };
|
||||
|
||||
/**
|
||||
* By default, when you set the display property, you replace the default one. This setting merges them instead.
|
||||
*/
|
||||
mergeDisplay?: boolean;
|
||||
|
||||
/**
|
||||
* A prop to add your own css classes to the keyboard wrapper. You can add multiple classes separated by a space.
|
||||
*/
|
||||
theme?: string;
|
||||
|
||||
/**
|
||||
* A prop to add your own css classes to one or several buttons.
|
||||
*/
|
||||
buttonTheme?: KeyboardButtonTheme[];
|
||||
|
||||
/**
|
||||
* A prop to add your own attributes to one or several buttons.
|
||||
*/
|
||||
buttonAttributes?: KeyboardButtonAttributes[];
|
||||
|
||||
/**
|
||||
* Runs a `console.log` every time a key is pressed. Displays the buttons pressed and the current input.
|
||||
*/
|
||||
debug?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies whether clicking the "ENTER" button will input a newline (`\n`) or not.
|
||||
*/
|
||||
newLineOnEnter?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies whether clicking the "TAB" button will input a tab character (`\t`) or not.
|
||||
*/
|
||||
tabCharOnTab?: boolean;
|
||||
|
||||
/**
|
||||
* Allows you to use a single simple-keyboard instance for several inputs.
|
||||
*/
|
||||
inputName?: string;
|
||||
|
||||
/**
|
||||
* `number`: Restrains all of simple-keyboard inputs to a certain length. This should be used in addition to the input element’s maxlengthattribute.
|
||||
*
|
||||
* `{ [inputName: string]: number }`: Restrains simple-keyboard’s individual inputs to a certain length. This should be used in addition to the input element’s maxlengthattribute.
|
||||
*/
|
||||
maxLength?: any;
|
||||
|
||||
/**
|
||||
* When set to true, this option synchronizes the internal input of every simple-keyboard instance.
|
||||
*/
|
||||
syncInstanceInputs?: boolean;
|
||||
|
||||
/**
|
||||
* Enable highlighting of keys pressed on physical keyboard.
|
||||
*/
|
||||
physicalKeyboardHighlight?: boolean;
|
||||
|
||||
/**
|
||||
* Presses keys highlighted by physicalKeyboardHighlight
|
||||
*/
|
||||
physicalKeyboardHighlightPress?: boolean;
|
||||
|
||||
/**
|
||||
* Define the text color that the physical keyboard highlighted key should have.
|
||||
*/
|
||||
physicalKeyboardHighlightTextColor?: string;
|
||||
|
||||
/**
|
||||
* Define the background color that the physical keyboard highlighted key should have.
|
||||
*/
|
||||
physicalKeyboardHighlightBgColor?: string;
|
||||
|
||||
/**
|
||||
* Calling preventDefault for the mousedown events keeps the focus on the input.
|
||||
*/
|
||||
preventMouseDownDefault?: boolean;
|
||||
|
||||
/**
|
||||
* Calling preventDefault for the mouseup events.
|
||||
*/
|
||||
preventMouseUpDefault?: boolean;
|
||||
|
||||
/**
|
||||
* Stops pointer down events on simple-keyboard buttons from bubbling to parent elements.
|
||||
*/
|
||||
stopMouseDownPropagation?: boolean;
|
||||
|
||||
/**
|
||||
* Stops pointer up events on simple-keyboard buttons from bubbling to parent elements.
|
||||
*/
|
||||
stopMouseUpPropagation?: boolean;
|
||||
|
||||
/**
|
||||
* Render buttons as a button element instead of a div element.
|
||||
*/
|
||||
useButtonTag?: boolean;
|
||||
|
||||
/**
|
||||
* A prop to ensure characters are always be added/removed at the end of the string.
|
||||
*/
|
||||
disableCaretPositioning?: boolean;
|
||||
|
||||
/**
|
||||
* Restrains input(s) change to the defined regular expression pattern.
|
||||
*/
|
||||
inputPattern?: any;
|
||||
|
||||
/**
|
||||
* Instructs simple-keyboard to use touch events instead of click events.
|
||||
*/
|
||||
useTouchEvents?: boolean;
|
||||
|
||||
/**
|
||||
* Enable useTouchEvents automatically when touch device is detected.
|
||||
*/
|
||||
autoUseTouchEvents?: boolean;
|
||||
|
||||
/**
|
||||
* Opt out of PointerEvents handling, falling back to the prior mouse event logic.
|
||||
*/
|
||||
useMouseEvents?: boolean;
|
||||
|
||||
/**
|
||||
* Disable button hold action.
|
||||
*/
|
||||
disableButtonHold?: boolean;
|
||||
|
||||
/**
|
||||
* Adds unicode right-to-left control characters to input return values.
|
||||
*/
|
||||
rtl?: boolean;
|
||||
|
||||
/**
|
||||
* Executes the callback function on key press. Returns button layout name (i.e.: "{shift}").
|
||||
*/
|
||||
onKeyPress?: (button: string) => any;
|
||||
|
||||
/**
|
||||
* Executes the callback function on key release.
|
||||
*/
|
||||
onKeyReleased?: (button: string) => any;
|
||||
|
||||
/**
|
||||
* Executes the callback function on input change. Returns the current input's string.
|
||||
*/
|
||||
onChange?: (input: string) => any;
|
||||
|
||||
/**
|
||||
* Executes the callback function before the first simple-keyboard render.
|
||||
*/
|
||||
beforeFirstRender?: () => void;
|
||||
|
||||
/**
|
||||
* Executes the callback function before a simple-keyboard render.
|
||||
*/
|
||||
beforeRender?: () => void;
|
||||
|
||||
/**
|
||||
* Executes the callback function every time simple-keyboard is rendered (e.g: when you change layouts).
|
||||
*/
|
||||
onRender?: () => void;
|
||||
|
||||
/**
|
||||
* Executes the callback function once simple-keyboard is rendered for the first time (on initialization).
|
||||
*/
|
||||
onInit?: () => void;
|
||||
|
||||
/**
|
||||
* Executes the callback function on input change. Returns the input object with all defined inputs.
|
||||
*/
|
||||
onChangeAll?: (inputs: any) => any;
|
||||
|
||||
/**
|
||||
* Module classes to be loaded by simple-keyboard.
|
||||
*/
|
||||
modules?: any[];
|
||||
|
||||
/**
|
||||
* Module options can have any format
|
||||
*/
|
||||
[name: string]: any;
|
||||
}
|
||||
|
||||
class Keyboard {
|
||||
constructor(selector: string, options: KeyboardOptions);
|
||||
constructor(selector: HTMLDivElement, options: KeyboardOptions);
|
||||
constructor(options: KeyboardOptions);
|
||||
|
||||
/**
|
||||
* Options
|
||||
*/
|
||||
options: KeyboardOptions;
|
||||
|
||||
/**
|
||||
* Utilities
|
||||
*/
|
||||
utilities?: any;
|
||||
|
||||
/**
|
||||
* caretPosition
|
||||
*/
|
||||
caretPosition?: number;
|
||||
|
||||
|
||||
/**
|
||||
* caretPositionEnd
|
||||
*/
|
||||
caretPositionEnd?: number;
|
||||
|
||||
/**
|
||||
* Changes the internal caret position
|
||||
* @param {number} position The caret's start position
|
||||
* @param {number} positionEnd The caret's end position
|
||||
*/
|
||||
setCaretPosition(position: number, positionEnd?: number): void;
|
||||
|
||||
/**
|
||||
* Retrieves the internal caret position
|
||||
*/
|
||||
getCaretPosition(): number;
|
||||
|
||||
/**
|
||||
* Retrieves the internal end caret position
|
||||
*/
|
||||
getCaretPositionEnd(): number;
|
||||
|
||||
/**
|
||||
* Adds/Modifies an entry to the `buttonTheme`. Basically a way to add a class to a button.
|
||||
* @param {string} buttons List of buttons to select (separated by a space).
|
||||
* @param {string} className Classes to give to the selected buttons (separated by space).
|
||||
*/
|
||||
addButtonTheme(buttons: string, className: string): void;
|
||||
|
||||
/**
|
||||
* Removes/Amends an entry to the `buttonTheme`. Basically a way to remove a class previously added to a button through buttonTheme or addButtonTheme.
|
||||
* @param {string} buttons List of buttons to select (separated by a space).
|
||||
* @param {string} className Classes to give to the selected buttons (separated by space).
|
||||
*/
|
||||
removeButtonTheme(buttons: string, className: string): void;
|
||||
|
||||
/**
|
||||
* Clear the keyboard's input.
|
||||
*
|
||||
* @param {string} [inputName] optional - the internal input to select
|
||||
*/
|
||||
clearInput(inputName?: string): void;
|
||||
|
||||
/**
|
||||
* Get the keyboard’s input (You can also get it from the onChange prop).
|
||||
* @param {string} [inputName] optional - the internal input to select
|
||||
*/
|
||||
getInput(inputName?: string): string;
|
||||
|
||||
/**
|
||||
* Set the keyboard’s input.
|
||||
* @param {string} input the input value
|
||||
* @param {string} inputName optional - the internal input to select
|
||||
*/
|
||||
setInput(input: string, inputName?: string): void;
|
||||
|
||||
/**
|
||||
* Replaces the entire internal input object.
|
||||
* @param {string} input the input object
|
||||
*/
|
||||
replaceInput(input: any): void;
|
||||
|
||||
/**
|
||||
* Set new option or modify existing ones after initialization.
|
||||
* @param {KeyboardOptions} option The option to set
|
||||
*/
|
||||
setOptions(options: KeyboardOptions): void;
|
||||
|
||||
/**
|
||||
* Send a command to all simple-keyboard instances at once (if you have multiple instances).
|
||||
* @param {function(instance: object, key: string)} callback Function to run on every instance
|
||||
*/
|
||||
dispatch(callback: (instance: any, key: string) => void): void;
|
||||
|
||||
/**
|
||||
* Get the DOM Element of a button. If there are several buttons with the same name, an array of the DOM Elements is returned.
|
||||
* @param {string} button The button layout name to select
|
||||
*/
|
||||
getButtonElement(button: string): HTMLElement | HTMLElement[];
|
||||
|
||||
/**
|
||||
* Clears keyboard listeners and DOM elements.
|
||||
*/
|
||||
destroy(): void;
|
||||
|
||||
/**
|
||||
* Iterate on each button (ForEach)
|
||||
*/
|
||||
recurseButtons(fn: any): void;
|
||||
}
|
||||
|
||||
export default Keyboard;
|
||||
}
|
@ -4,6 +4,13 @@ import "./Keyboard.css";
|
||||
import { getDefaultLayout } from "../services/KeyboardLayout";
|
||||
import PhysicalKeyboard from "../services/PhysicalKeyboard";
|
||||
import Utilities from "../services/Utilities";
|
||||
import {
|
||||
KeyboardOptions,
|
||||
KeyboardInput,
|
||||
KeyboardButtonElements,
|
||||
KeyboardHandlerEvent,
|
||||
KeyboardButton,
|
||||
} from "../interfaces";
|
||||
|
||||
/**
|
||||
* Root class for simple-keyboard
|
||||
@ -13,11 +20,32 @@ import Utilities from "../services/Utilities";
|
||||
* - Handles button functionality
|
||||
*/
|
||||
class SimpleKeyboard {
|
||||
input: KeyboardInput;
|
||||
options: KeyboardOptions;
|
||||
utilities: any;
|
||||
caretPosition: number;
|
||||
caretPositionEnd: number;
|
||||
keyboardDOM: KeyboardButton;
|
||||
keyboardPluginClasses: string;
|
||||
keyboardDOMClass: string;
|
||||
buttonElements: KeyboardButtonElements;
|
||||
currentInstanceName: string;
|
||||
allKeyboardInstances: { [key: string]: SimpleKeyboard };
|
||||
keyboardInstanceNames: string[];
|
||||
isFirstKeyboardInstance: boolean;
|
||||
physicalKeyboard: PhysicalKeyboard;
|
||||
modules: { [key: string]: any };
|
||||
activeButtonClass: string;
|
||||
holdInteractionTimeout: number;
|
||||
holdTimeout: number;
|
||||
isMouseHold: boolean;
|
||||
initialized: boolean;
|
||||
|
||||
/**
|
||||
* Creates an instance of SimpleKeyboard
|
||||
* @param {Array} params If first parameter is a string, it is considered the container class. The second parameter is then considered the options object. If first parameter is an object, it is considered the options object.
|
||||
*/
|
||||
constructor(...params) {
|
||||
constructor(...params: []) {
|
||||
const { keyboardDOMClass, keyboardDOM, options = {} } = this.handleParams(
|
||||
params
|
||||
);
|
||||
@ -29,7 +57,7 @@ class SimpleKeyboard {
|
||||
getOptions: this.getOptions,
|
||||
getCaretPosition: this.getCaretPosition,
|
||||
getCaretPositionEnd: this.getCaretPositionEnd,
|
||||
dispatch: this.dispatch
|
||||
dispatch: this.dispatch,
|
||||
});
|
||||
|
||||
/**
|
||||
@ -155,7 +183,7 @@ class SimpleKeyboard {
|
||||
*/
|
||||
this.physicalKeyboard = new PhysicalKeyboard({
|
||||
dispatch: this.dispatch,
|
||||
getOptions: this.getOptions
|
||||
getOptions: this.getOptions,
|
||||
});
|
||||
|
||||
/**
|
||||
@ -177,7 +205,13 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* parseParams
|
||||
*/
|
||||
handleParams = params => {
|
||||
handleParams = (
|
||||
params: any[]
|
||||
): {
|
||||
keyboardDOMClass: string;
|
||||
keyboardDOM: KeyboardButton;
|
||||
options: Partial<KeyboardOptions>;
|
||||
} => {
|
||||
let keyboardDOMClass;
|
||||
let keyboardDOM;
|
||||
let options;
|
||||
@ -188,11 +222,13 @@ class SimpleKeyboard {
|
||||
*/
|
||||
if (typeof params[0] === "string") {
|
||||
keyboardDOMClass = params[0].split(".").join("");
|
||||
keyboardDOM = document.querySelector(`.${keyboardDOMClass}`);
|
||||
keyboardDOM = document.querySelector(
|
||||
`.${keyboardDOMClass}`
|
||||
) as KeyboardButton;
|
||||
options = params[1];
|
||||
|
||||
/**
|
||||
* If first parameter is an HTMLDivElement
|
||||
* If first parameter is an KeyboardButton
|
||||
* Consider it as the keyboard DOM element
|
||||
*/
|
||||
} else if (params[0] instanceof HTMLDivElement) {
|
||||
@ -213,43 +249,47 @@ class SimpleKeyboard {
|
||||
*/
|
||||
} else {
|
||||
keyboardDOMClass = "simple-keyboard";
|
||||
keyboardDOM = document.querySelector(`.${keyboardDOMClass}`);
|
||||
keyboardDOM = document.querySelector(
|
||||
`.${keyboardDOMClass}`
|
||||
) as KeyboardButton;
|
||||
options = params[0];
|
||||
}
|
||||
|
||||
return {
|
||||
keyboardDOMClass,
|
||||
keyboardDOM,
|
||||
options
|
||||
options,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Getters
|
||||
*/
|
||||
getOptions = () => this.options;
|
||||
getCaretPosition = () => this.caretPosition;
|
||||
getCaretPositionEnd = () => this.caretPositionEnd;
|
||||
getOptions = (): KeyboardOptions => this.options;
|
||||
getCaretPosition = (): number => this.caretPosition;
|
||||
getCaretPositionEnd = (): number => this.caretPositionEnd;
|
||||
|
||||
/**
|
||||
* Setters
|
||||
* Changes the internal caret position
|
||||
* @param {number} position The caret's start position
|
||||
* @param {number} positionEnd The caret's end position
|
||||
*/
|
||||
setCaretPosition(position, endPosition) {
|
||||
setCaretPosition(position: number, endPosition = position): void {
|
||||
this.caretPosition = position;
|
||||
this.caretPositionEnd = endPosition || position;
|
||||
this.caretPositionEnd = endPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles clicks made to keyboard buttons
|
||||
* @param {string} button The button's layout name.
|
||||
*/
|
||||
handleButtonClicked(button) {
|
||||
handleButtonClicked(button: string): void {
|
||||
const debug = this.options.debug;
|
||||
|
||||
/**
|
||||
* Ignoring placeholder buttons
|
||||
*/
|
||||
if (button === "{//}") return false;
|
||||
if (button === "{//}") return;
|
||||
|
||||
/**
|
||||
* Calling onKeyPress
|
||||
@ -283,7 +323,7 @@ class SimpleKeyboard {
|
||||
this.options.maxLength &&
|
||||
this.utilities.handleMaxLength(this.input, updatedInput)
|
||||
) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.input[this.options.inputName] = this.utilities.getUpdatedInput(
|
||||
@ -332,7 +372,7 @@ class SimpleKeyboard {
|
||||
* Handles button mousedown
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
handleButtonMouseDown(button, e) {
|
||||
handleButtonMouseDown(button: string, e: KeyboardHandlerEvent): void {
|
||||
if (e) {
|
||||
/**
|
||||
* Handle event options
|
||||
@ -358,7 +398,7 @@ class SimpleKeyboard {
|
||||
* @type {object} Time to wait until a key hold is detected
|
||||
*/
|
||||
if (!this.options.disableButtonHold) {
|
||||
this.holdTimeout = setTimeout(() => {
|
||||
this.holdTimeout = window.setTimeout(() => {
|
||||
if (
|
||||
(this.isMouseHold &&
|
||||
// TODO: This needs to be configurable through options
|
||||
@ -375,7 +415,7 @@ class SimpleKeyboard {
|
||||
) {
|
||||
if (this.options.debug) console.log("Button held:", button);
|
||||
|
||||
this.handleButtonHold(button, e);
|
||||
this.handleButtonHold(button);
|
||||
}
|
||||
clearTimeout(this.holdTimeout);
|
||||
}, 500);
|
||||
@ -385,7 +425,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Handles button mouseup
|
||||
*/
|
||||
handleButtonMouseUp(button = null, e = null) {
|
||||
handleButtonMouseUp(button?: string, e?: KeyboardHandlerEvent): void {
|
||||
if (e) {
|
||||
/**
|
||||
* Handle event options
|
||||
@ -397,7 +437,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Remove active class
|
||||
*/
|
||||
this.recurseButtons(buttonElement => {
|
||||
this.recurseButtons((buttonElement: Element) => {
|
||||
buttonElement.classList.remove(this.activeButtonClass);
|
||||
});
|
||||
|
||||
@ -414,7 +454,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Handles container mousedown
|
||||
*/
|
||||
handleKeyboardContainerMouseDown(e) {
|
||||
handleKeyboardContainerMouseDown(e: KeyboardHandlerEvent): void {
|
||||
/**
|
||||
* Handle event options
|
||||
*/
|
||||
@ -425,13 +465,13 @@ class SimpleKeyboard {
|
||||
* Handles button hold
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
handleButtonHold(button) {
|
||||
handleButtonHold(button: string): void {
|
||||
if (this.holdInteractionTimeout) clearTimeout(this.holdInteractionTimeout);
|
||||
|
||||
/**
|
||||
* @type {object} Timeout dictating the speed of key hold iterations
|
||||
*/
|
||||
this.holdInteractionTimeout = setTimeout(() => {
|
||||
this.holdInteractionTimeout = window.setTimeout(() => {
|
||||
if (this.isMouseHold) {
|
||||
this.handleButtonClicked(button);
|
||||
this.handleButtonHold(button);
|
||||
@ -444,8 +484,8 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Send a command to all simple-keyboard instances (if you have several instances).
|
||||
*/
|
||||
syncInstanceInputs() {
|
||||
this.dispatch(instance => {
|
||||
syncInstanceInputs(): void {
|
||||
this.dispatch((instance: SimpleKeyboard) => {
|
||||
instance.replaceInput(this.input);
|
||||
instance.setCaretPosition(this.caretPosition, this.caretPositionEnd);
|
||||
});
|
||||
@ -455,7 +495,7 @@ class SimpleKeyboard {
|
||||
* Clear the keyboard’s input.
|
||||
* @param {string} [inputName] optional - the internal input to select
|
||||
*/
|
||||
clearInput(inputName) {
|
||||
clearInput(inputName: string): void {
|
||||
inputName = inputName || this.options.inputName;
|
||||
this.input[inputName] = "";
|
||||
|
||||
@ -474,7 +514,7 @@ class SimpleKeyboard {
|
||||
* Get the keyboard’s input (You can also get it from the onChange prop).
|
||||
* @param {string} [inputName] optional - the internal input to select
|
||||
*/
|
||||
getInput(inputName, skipSync = false) {
|
||||
getInput(inputName: string, skipSync = false): string {
|
||||
inputName = inputName || this.options.inputName;
|
||||
|
||||
/**
|
||||
@ -497,11 +537,11 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Get all simple-keyboard inputs
|
||||
*/
|
||||
getAllInputs() {
|
||||
getAllInputs(): KeyboardInput {
|
||||
const output = {};
|
||||
const inputNames = Object.keys(this.input);
|
||||
|
||||
inputNames.forEach(inputName => {
|
||||
inputNames.forEach((inputName) => {
|
||||
output[inputName] = this.getInput(inputName, true);
|
||||
});
|
||||
|
||||
@ -513,7 +553,7 @@ class SimpleKeyboard {
|
||||
* @param {string} input the input value
|
||||
* @param {string} inputName optional - the internal input to select
|
||||
*/
|
||||
setInput(input, inputName) {
|
||||
setInput(input: string, inputName: string): void {
|
||||
inputName = inputName || this.options.inputName;
|
||||
this.input[inputName] = input;
|
||||
|
||||
@ -527,7 +567,7 @@ class SimpleKeyboard {
|
||||
* Replace the input object (`keyboard.input`)
|
||||
* @param {object} inputObj The input object
|
||||
*/
|
||||
replaceInput(inputObj) {
|
||||
replaceInput(inputObj: KeyboardInput): void {
|
||||
this.input = inputObj;
|
||||
}
|
||||
|
||||
@ -535,7 +575,7 @@ class SimpleKeyboard {
|
||||
* Set new option or modify existing ones after initialization.
|
||||
* @param {object} options The options to set
|
||||
*/
|
||||
setOptions(options = {}) {
|
||||
setOptions(options = {}): void {
|
||||
const changedOptions = this.changedOptions(options);
|
||||
this.options = Object.assign(this.options, options);
|
||||
|
||||
@ -560,9 +600,9 @@ class SimpleKeyboard {
|
||||
* Detecting changes to non-function options
|
||||
* This allows us to ascertain whether a button re-render is needed
|
||||
*/
|
||||
changedOptions(newOptions) {
|
||||
changedOptions(newOptions: Partial<KeyboardOptions>): string[] {
|
||||
return Object.keys(newOptions).filter(
|
||||
optionName =>
|
||||
(optionName) =>
|
||||
JSON.stringify(newOptions[optionName]) !==
|
||||
JSON.stringify(this.options[optionName])
|
||||
);
|
||||
@ -572,7 +612,7 @@ class SimpleKeyboard {
|
||||
* Executing actions depending on changed options
|
||||
* @param {object} options The options to set
|
||||
*/
|
||||
onSetOptions(options) {
|
||||
onSetOptions(options: Partial<KeyboardOptions>): void {
|
||||
if (options.inputName) {
|
||||
/**
|
||||
* inputName changed. This requires a caretPosition reset
|
||||
@ -588,7 +628,7 @@ class SimpleKeyboard {
|
||||
* Remove all keyboard rows and reset keyboard values.
|
||||
* Used internally between re-renders.
|
||||
*/
|
||||
clear() {
|
||||
clear(): void {
|
||||
this.keyboardDOM.innerHTML = "";
|
||||
this.keyboardDOM.className = this.keyboardDOMClass;
|
||||
this.buttonElements = {};
|
||||
@ -598,7 +638,8 @@ class SimpleKeyboard {
|
||||
* Send a command to all simple-keyboard instances at once (if you have multiple instances).
|
||||
* @param {function(instance: object, key: string)} callback Function to run on every instance
|
||||
*/
|
||||
dispatch(callback) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
dispatch(callback: (instance: SimpleKeyboard, key?: string) => void): void {
|
||||
if (!window["SimpleKeyboardInstances"]) {
|
||||
console.warn(
|
||||
`SimpleKeyboardInstances is not defined. Dispatch cannot be called.`
|
||||
@ -606,7 +647,7 @@ class SimpleKeyboard {
|
||||
throw new Error("INSTANCES_VAR_ERROR");
|
||||
}
|
||||
|
||||
return Object.keys(window["SimpleKeyboardInstances"]).forEach(key => {
|
||||
return Object.keys(window["SimpleKeyboardInstances"]).forEach((key) => {
|
||||
callback(window["SimpleKeyboardInstances"][key], key);
|
||||
});
|
||||
}
|
||||
@ -616,11 +657,11 @@ class SimpleKeyboard {
|
||||
* @param {string} buttons List of buttons to select (separated by a space).
|
||||
* @param {string} className Classes to give to the selected buttons (separated by space).
|
||||
*/
|
||||
addButtonTheme(buttons, className) {
|
||||
if (!className || !buttons) return false;
|
||||
addButtonTheme(buttons: string, className: string): void {
|
||||
if (!className || !buttons) return;
|
||||
|
||||
buttons.split(" ").forEach(button => {
|
||||
className.split(" ").forEach(classNameItem => {
|
||||
buttons.split(" ").forEach((button) => {
|
||||
className.split(" ").forEach((classNameItem) => {
|
||||
if (!this.options.buttonTheme) this.options.buttonTheme = [];
|
||||
|
||||
let classNameFound = false;
|
||||
@ -628,7 +669,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* If class is already defined, we add button to class definition
|
||||
*/
|
||||
this.options.buttonTheme.map(buttonTheme => {
|
||||
this.options.buttonTheme.map((buttonTheme) => {
|
||||
if (buttonTheme.class.split(" ").includes(classNameItem)) {
|
||||
classNameFound = true;
|
||||
|
||||
@ -648,7 +689,7 @@ class SimpleKeyboard {
|
||||
if (!classNameFound) {
|
||||
this.options.buttonTheme.push({
|
||||
class: classNameItem,
|
||||
buttons: buttons
|
||||
buttons: buttons,
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -662,14 +703,14 @@ class SimpleKeyboard {
|
||||
* @param {string} buttons List of buttons to select (separated by a space).
|
||||
* @param {string} className Classes to give to the selected buttons (separated by space).
|
||||
*/
|
||||
removeButtonTheme(buttons, className) {
|
||||
removeButtonTheme(buttons: string, className: string): void {
|
||||
/**
|
||||
* When called with empty parameters, remove all button themes
|
||||
*/
|
||||
if (!buttons && !className) {
|
||||
this.options.buttonTheme = [];
|
||||
this.render();
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -681,7 +722,7 @@ class SimpleKeyboard {
|
||||
this.options.buttonTheme.length
|
||||
) {
|
||||
const buttonArray = buttons.split(" ");
|
||||
buttonArray.forEach(button => {
|
||||
buttonArray.forEach((button) => {
|
||||
this.options.buttonTheme.map((buttonTheme, index) => {
|
||||
/**
|
||||
* If className is set, we affect the buttons only for that class
|
||||
@ -693,7 +734,7 @@ class SimpleKeyboard {
|
||||
) {
|
||||
const filteredButtonArray = buttonTheme.buttons
|
||||
.split(" ")
|
||||
.filter(item => item !== button);
|
||||
.filter((item) => item !== button);
|
||||
|
||||
/**
|
||||
* If buttons left, return them, otherwise, remove button Theme
|
||||
@ -718,7 +759,7 @@ class SimpleKeyboard {
|
||||
* Get the DOM Element of a button. If there are several buttons with the same name, an array of the DOM Elements is returned.
|
||||
* @param {string} button The button layout name to select
|
||||
*/
|
||||
getButtonElement(button) {
|
||||
getButtonElement(button: string): KeyboardButton | KeyboardButton[] {
|
||||
let output;
|
||||
|
||||
const buttonArr = this.buttonElements[button];
|
||||
@ -737,7 +778,7 @@ class SimpleKeyboard {
|
||||
* This handles the "inputPattern" option
|
||||
* by checking if the provided inputPattern passes
|
||||
*/
|
||||
inputPatternIsValid(inputVal) {
|
||||
inputPatternIsValid(inputVal: string): boolean {
|
||||
const inputPatternRaw = this.options.inputPattern;
|
||||
let inputPattern;
|
||||
|
||||
@ -773,7 +814,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Handles simple-keyboard event listeners
|
||||
*/
|
||||
setEventListeners() {
|
||||
setEventListeners(): void {
|
||||
/**
|
||||
* Only first instance should set the event listeners
|
||||
*/
|
||||
@ -795,7 +836,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Event Handler: KeyUp
|
||||
*/
|
||||
handleKeyUp(event) {
|
||||
handleKeyUp(event: KeyboardHandlerEvent): void {
|
||||
this.caretEventHandler(event);
|
||||
|
||||
if (this.options.physicalKeyboardHighlight) {
|
||||
@ -806,7 +847,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Event Handler: KeyDown
|
||||
*/
|
||||
handleKeyDown(event) {
|
||||
handleKeyDown(event: KeyboardHandlerEvent): void {
|
||||
if (this.options.physicalKeyboardHighlight) {
|
||||
this.physicalKeyboard.handleHighlightKeyDown(event);
|
||||
}
|
||||
@ -815,7 +856,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Event Handler: MouseUp
|
||||
*/
|
||||
handleMouseUp(event) {
|
||||
handleMouseUp(event: KeyboardHandlerEvent): void {
|
||||
this.caretEventHandler(event);
|
||||
}
|
||||
|
||||
@ -823,20 +864,20 @@ class SimpleKeyboard {
|
||||
* Event Handler: TouchEnd
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
handleTouchEnd(event) {
|
||||
handleTouchEnd(event: KeyboardHandlerEvent): void {
|
||||
this.caretEventHandler(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link setEventListeners} when an event that warrants a cursor position update is triggered
|
||||
*/
|
||||
caretEventHandler(event) {
|
||||
let targetTagName;
|
||||
caretEventHandler(event: KeyboardHandlerEvent): void {
|
||||
let targetTagName: string;
|
||||
if (event.target.tagName) {
|
||||
targetTagName = event.target.tagName.toLowerCase();
|
||||
}
|
||||
|
||||
this.dispatch(instance => {
|
||||
this.dispatch((instance) => {
|
||||
const isKeyboard =
|
||||
event.target === instance.keyboardDOM ||
|
||||
(event.target && instance.keyboardDOM.contains(event.target));
|
||||
@ -881,10 +922,10 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Execute an operation on each button
|
||||
*/
|
||||
recurseButtons(fn) {
|
||||
recurseButtons(fn: any): void {
|
||||
if (!fn) return;
|
||||
|
||||
Object.keys(this.buttonElements).forEach(buttonName =>
|
||||
Object.keys(this.buttonElements).forEach((buttonName) =>
|
||||
this.buttonElements[buttonName].forEach(fn)
|
||||
);
|
||||
}
|
||||
@ -892,7 +933,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Destroy keyboard listeners and DOM elements
|
||||
*/
|
||||
destroy() {
|
||||
destroy(): void {
|
||||
if (this.options.debug)
|
||||
console.log(
|
||||
`Destroying simple-keyboard instance: ${this.currentInstanceName}`
|
||||
@ -913,7 +954,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Remove buttons
|
||||
*/
|
||||
let deleteButton = buttonElement => {
|
||||
let deleteButton = (buttonElement: KeyboardButton) => {
|
||||
buttonElement.onpointerdown = null;
|
||||
buttonElement.onpointerup = null;
|
||||
buttonElement.onpointercancel = null;
|
||||
@ -960,12 +1001,12 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Process buttonTheme option
|
||||
*/
|
||||
getButtonThemeClasses(button) {
|
||||
getButtonThemeClasses(button: string): string[] {
|
||||
const buttonTheme = this.options.buttonTheme;
|
||||
let buttonClasses = [];
|
||||
let buttonClasses: string[] = [];
|
||||
|
||||
if (Array.isArray(buttonTheme)) {
|
||||
buttonTheme.forEach(themeObj => {
|
||||
buttonTheme.forEach((themeObj) => {
|
||||
if (
|
||||
themeObj.class &&
|
||||
typeof themeObj.class === "string" &&
|
||||
@ -993,11 +1034,11 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Process buttonAttributes option
|
||||
*/
|
||||
setDOMButtonAttributes(button, callback) {
|
||||
setDOMButtonAttributes(button: string, callback: any): void {
|
||||
const buttonAttributes = this.options.buttonAttributes;
|
||||
|
||||
if (Array.isArray(buttonAttributes)) {
|
||||
buttonAttributes.forEach(attrObj => {
|
||||
buttonAttributes.forEach((attrObj) => {
|
||||
if (
|
||||
attrObj.attribute &&
|
||||
typeof attrObj.attribute === "string" &&
|
||||
@ -1038,7 +1079,7 @@ class SimpleKeyboard {
|
||||
*/
|
||||
/* istanbul ignore next */
|
||||
disableContextualWindow() {
|
||||
window.oncontextmenu = event => {
|
||||
window.oncontextmenu = (event: KeyboardHandlerEvent) => {
|
||||
if (event.target.classList.contains("hg-button")) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
@ -1144,7 +1185,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Register module
|
||||
*/
|
||||
registerModule = (name, initCallback) => {
|
||||
registerModule = (name: string, initCallback: any) => {
|
||||
if (!this.modules[name]) this.modules[name] = {};
|
||||
|
||||
initCallback(this.modules[name]);
|
||||
@ -1155,7 +1196,7 @@ class SimpleKeyboard {
|
||||
*/
|
||||
loadModules() {
|
||||
if (Array.isArray(this.options.modules)) {
|
||||
this.options.modules.forEach(KeyboardModule => {
|
||||
this.options.modules.forEach((KeyboardModule) => {
|
||||
const keyboardModule = new KeyboardModule();
|
||||
keyboardModule.init(this);
|
||||
});
|
||||
@ -1170,7 +1211,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Get module prop
|
||||
*/
|
||||
getModuleProp(name, prop) {
|
||||
getModuleProp(name: string, prop: string) {
|
||||
if (!this.modules[name]) return false;
|
||||
|
||||
return this.modules[name][prop];
|
||||
@ -1187,10 +1228,10 @@ class SimpleKeyboard {
|
||||
* Parse Row DOM containers
|
||||
*/
|
||||
parseRowDOMContainers(
|
||||
rowDOM,
|
||||
rowIndex,
|
||||
containerStartIndexes,
|
||||
containerEndIndexes
|
||||
rowDOM: HTMLDivElement,
|
||||
rowIndex: number,
|
||||
containerStartIndexes: number[],
|
||||
containerEndIndexes: number[]
|
||||
) {
|
||||
const rowDOMArray = Array.from(rowDOM.children);
|
||||
let removedElements = 0;
|
||||
@ -1235,7 +1276,9 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Inserting elements to container
|
||||
*/
|
||||
containedElements.forEach(element => containerDOM.appendChild(element));
|
||||
containedElements.forEach((element) =>
|
||||
containerDOM.appendChild(element)
|
||||
);
|
||||
|
||||
/**
|
||||
* Adding container at correct position within rowDOMArray
|
||||
@ -1250,7 +1293,7 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Appending rowDOM new children list
|
||||
*/
|
||||
rowDOMArray.forEach(element => rowDOM.appendChild(element));
|
||||
rowDOMArray.forEach((element) => rowDOM.appendChild(element));
|
||||
|
||||
if (this.options.debug) {
|
||||
console.log(
|
||||
@ -1270,9 +1313,9 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* getKeyboardClassString
|
||||
*/
|
||||
getKeyboardClassString = (...baseDOMClasses) => {
|
||||
getKeyboardClassString = (...baseDOMClasses: any[]) => {
|
||||
const keyboardClasses = [this.keyboardDOMClass, ...baseDOMClasses].filter(
|
||||
DOMClass => !!DOMClass
|
||||
(DOMClass) => !!DOMClass
|
||||
);
|
||||
|
||||
return keyboardClasses.join(" ");
|
||||
@ -1331,8 +1374,8 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Tracking container indicators in rows
|
||||
*/
|
||||
const containerStartIndexes = [];
|
||||
const containerEndIndexes = [];
|
||||
const containerStartIndexes: number[] = [];
|
||||
const containerEndIndexes: number[] = [];
|
||||
|
||||
/**
|
||||
* Iterating through each button in row
|
||||
@ -1399,9 +1442,12 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Adding buttonAttributes
|
||||
*/
|
||||
this.setDOMButtonAttributes(button, (attribute, value) => {
|
||||
buttonDOM.setAttribute(attribute, value);
|
||||
});
|
||||
this.setDOMButtonAttributes(
|
||||
button,
|
||||
(attribute: string, value: string) => {
|
||||
buttonDOM.setAttribute(attribute, value);
|
||||
}
|
||||
);
|
||||
|
||||
this.activeButtonClass = "hg-activeButton";
|
||||
|
||||
@ -1417,14 +1463,14 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Handle PointerEvents
|
||||
*/
|
||||
buttonDOM.onpointerdown = e => {
|
||||
buttonDOM.onpointerdown = (e: KeyboardHandlerEvent) => {
|
||||
this.handleButtonClicked(button);
|
||||
this.handleButtonMouseDown(button, e);
|
||||
};
|
||||
buttonDOM.onpointerup = e => {
|
||||
buttonDOM.onpointerup = (e: KeyboardHandlerEvent) => {
|
||||
this.handleButtonMouseUp(button, e);
|
||||
};
|
||||
buttonDOM.onpointercancel = e => {
|
||||
buttonDOM.onpointercancel = (e: KeyboardHandlerEvent) => {
|
||||
this.handleButtonMouseUp(button, e);
|
||||
};
|
||||
} else {
|
||||
@ -1435,14 +1481,14 @@ class SimpleKeyboard {
|
||||
/**
|
||||
* Handle touch events
|
||||
*/
|
||||
buttonDOM.ontouchstart = e => {
|
||||
buttonDOM.ontouchstart = (e: KeyboardHandlerEvent) => {
|
||||
this.handleButtonClicked(button);
|
||||
this.handleButtonMouseDown(button, e);
|
||||
};
|
||||
buttonDOM.ontouchend = e => {
|
||||
buttonDOM.ontouchend = (e: KeyboardHandlerEvent) => {
|
||||
this.handleButtonMouseUp(button, e);
|
||||
};
|
||||
buttonDOM.ontouchcancel = e => {
|
||||
buttonDOM.ontouchcancel = (e: KeyboardHandlerEvent) => {
|
||||
this.handleButtonMouseUp(button, e);
|
||||
};
|
||||
} else {
|
||||
@ -1453,10 +1499,10 @@ class SimpleKeyboard {
|
||||
this.isMouseHold = false;
|
||||
this.handleButtonClicked(button);
|
||||
};
|
||||
buttonDOM.onmousedown = e => {
|
||||
buttonDOM.onmousedown = (e: KeyboardHandlerEvent) => {
|
||||
this.handleButtonMouseDown(button, e);
|
||||
};
|
||||
buttonDOM.onmouseup = e => {
|
||||
buttonDOM.onmouseup = (e: KeyboardHandlerEvent) => {
|
||||
this.handleButtonMouseUp(button, e);
|
||||
};
|
||||
}
|
||||
@ -1531,7 +1577,7 @@ class SimpleKeyboard {
|
||||
!useMouseEvents
|
||||
) {
|
||||
document.onpointerup = () => this.handleButtonMouseUp();
|
||||
this.keyboardDOM.onpointerdown = e =>
|
||||
this.keyboardDOM.onpointerdown = (e: KeyboardHandlerEvent) =>
|
||||
this.handleKeyboardContainerMouseDown(e);
|
||||
} else if (useTouchEvents) {
|
||||
/**
|
||||
@ -1540,14 +1586,14 @@ class SimpleKeyboard {
|
||||
document.ontouchend = () => this.handleButtonMouseUp();
|
||||
document.ontouchcancel = () => this.handleButtonMouseUp();
|
||||
|
||||
this.keyboardDOM.ontouchstart = e =>
|
||||
this.keyboardDOM.ontouchstart = (e: KeyboardHandlerEvent) =>
|
||||
this.handleKeyboardContainerMouseDown(e);
|
||||
} else if (!useTouchEvents) {
|
||||
/**
|
||||
* Handling mouseup
|
||||
*/
|
||||
document.onmouseup = () => this.handleButtonMouseUp();
|
||||
this.keyboardDOM.onmousedown = e =>
|
||||
this.keyboardDOM.onmousedown = (e: KeyboardHandlerEvent) =>
|
||||
this.handleKeyboardContainerMouseDown(e);
|
||||
}
|
||||
|
186
src/lib/interfaces.ts
Normal file
186
src/lib/interfaces.ts
Normal file
@ -0,0 +1,186 @@
|
||||
export interface KeyboardLayoutObject {
|
||||
[key: string]: string[];
|
||||
}
|
||||
|
||||
export interface KeyboardButtonTheme {
|
||||
class: string;
|
||||
buttons: string;
|
||||
}
|
||||
|
||||
export interface KeyboardButtonAttributes {
|
||||
attribute: string;
|
||||
value: string;
|
||||
buttons: string;
|
||||
}
|
||||
|
||||
export interface KeyboardInput {
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
export type KeyboardButton = HTMLDivElement | HTMLButtonElement;
|
||||
export type KeyboardHandlerEvent = PointerEvent & TouchEvent & KeyboardEvent & { target: HTMLDivElement | HTMLInputElement };
|
||||
|
||||
export interface KeyboardButtonElements {
|
||||
[key: string]: KeyboardButton[]
|
||||
}
|
||||
|
||||
export interface UtilitiesParams {
|
||||
getOptions: () => KeyboardOptions;
|
||||
getCaretPosition: () => number;
|
||||
getCaretPositionEnd: () => number;
|
||||
dispatch: any;
|
||||
}
|
||||
|
||||
export interface KeyboardOptions {
|
||||
/**
|
||||
* Modify the keyboard layout.
|
||||
*/
|
||||
layout?: KeyboardLayoutObject;
|
||||
|
||||
/**
|
||||
* Specifies which layout should be used.
|
||||
*/
|
||||
layoutName?: string;
|
||||
|
||||
/**
|
||||
* Replaces variable buttons (such as `{bksp}`) with a human-friendly name (e.g.: `backspace`).
|
||||
*/
|
||||
display?: { [button: string]: string };
|
||||
|
||||
/**
|
||||
* By default, when you set the display property, you replace the default one. This setting merges them instead.
|
||||
*/
|
||||
mergeDisplay?: boolean;
|
||||
|
||||
/**
|
||||
* A prop to add your own css classes to the keyboard wrapper. You can add multiple classes separated by a space.
|
||||
*/
|
||||
theme?: string;
|
||||
|
||||
/**
|
||||
* A prop to add your own css classes to one or several buttons.
|
||||
*/
|
||||
buttonTheme?: KeyboardButtonTheme[];
|
||||
|
||||
/**
|
||||
* A prop to add your own attributes to one or several buttons.
|
||||
*/
|
||||
buttonAttributes?: KeyboardButtonAttributes[];
|
||||
|
||||
/**
|
||||
* Runs a `console.log` every time a key is pressed. Displays the buttons pressed and the current input.
|
||||
*/
|
||||
debug?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies whether clicking the "ENTER" button will input a newline (`\n`) or not.
|
||||
*/
|
||||
newLineOnEnter?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies whether clicking the "TAB" button will input a tab character (`\t`) or not.
|
||||
*/
|
||||
tabCharOnTab?: boolean;
|
||||
|
||||
/**
|
||||
* Allows you to use a single simple-keyboard instance for several inputs.
|
||||
*/
|
||||
inputName?: string;
|
||||
|
||||
/**
|
||||
* `number`: Restrains all of simple-keyboard inputs to a certain length. This should be used in addition to the input element’s maxlengthattribute.
|
||||
*
|
||||
* `{ [inputName: string]: number }`: Restrains simple-keyboard’s individual inputs to a certain length. This should be used in addition to the input element’s maxlengthattribute.
|
||||
*/
|
||||
maxLength?: any;
|
||||
|
||||
/**
|
||||
* When set to true, this option synchronizes the internal input of every simple-keyboard instance.
|
||||
*/
|
||||
syncInstanceInputs?: boolean;
|
||||
|
||||
/**
|
||||
* Enable highlighting of keys pressed on physical keyboard.
|
||||
*/
|
||||
physicalKeyboardHighlight?: boolean;
|
||||
|
||||
/**
|
||||
* Presses keys highlighted by physicalKeyboardHighlight
|
||||
*/
|
||||
physicalKeyboardHighlightPress?: boolean;
|
||||
|
||||
/**
|
||||
* Define the text color that the physical keyboard highlighted key should have.
|
||||
*/
|
||||
physicalKeyboardHighlightTextColor?: string;
|
||||
|
||||
/**
|
||||
* Define the background color that the physical keyboard highlighted key should have.
|
||||
*/
|
||||
physicalKeyboardHighlightBgColor?: string;
|
||||
|
||||
/**
|
||||
* Calling preventDefault for the mousedown events keeps the focus on the input.
|
||||
*/
|
||||
preventMouseDownDefault?: boolean;
|
||||
|
||||
/**
|
||||
* Calling preventDefault for the mouseup events.
|
||||
*/
|
||||
preventMouseUpDefault?: boolean;
|
||||
|
||||
/**
|
||||
* Stops pointer down events on simple-keyboard buttons from bubbling to parent elements.
|
||||
*/
|
||||
stopMouseDownPropagation?: boolean;
|
||||
|
||||
/**
|
||||
* Stops pointer up events on simple-keyboard buttons from bubbling to parent elements.
|
||||
*/
|
||||
stopMouseUpPropagation?: boolean;
|
||||
|
||||
/**
|
||||
* Render buttons as a button element instead of a div element.
|
||||
*/
|
||||
useButtonTag?: boolean;
|
||||
|
||||
/**
|
||||
* A prop to ensure characters are always be added/removed at the end of the string.
|
||||
*/
|
||||
disableCaretPositioning?: boolean;
|
||||
|
||||
/**
|
||||
* Restrains input(s) change to the defined regular expression pattern.
|
||||
*/
|
||||
inputPattern?: any;
|
||||
|
||||
/**
|
||||
* Instructs simple-keyboard to use touch events instead of click events.
|
||||
*/
|
||||
useTouchEvents?: boolean;
|
||||
|
||||
/**
|
||||
* Enable useTouchEvents automatically when touch device is detected.
|
||||
*/
|
||||
autoUseTouchEvents?: boolean;
|
||||
|
||||
/**
|
||||
* Opt out of PointerEvents handling, falling back to the prior mouse event logic.
|
||||
*/
|
||||
useMouseEvents?: boolean;
|
||||
|
||||
/**
|
||||
* Disable button hold action.
|
||||
*/
|
||||
disableButtonHold?: boolean;
|
||||
|
||||
/**
|
||||
* Adds unicode right-to-left control characters to input return values.
|
||||
*/
|
||||
rtl?: boolean;
|
||||
|
||||
/**
|
||||
* Module options can have any format
|
||||
*/
|
||||
[name: string]: any;
|
||||
}
|
@ -5,14 +5,14 @@ export const getDefaultLayout = () => {
|
||||
"{tab} q w e r t y u i o p [ ] \\",
|
||||
"{lock} a s d f g h j k l ; ' {enter}",
|
||||
"{shift} z x c v b n m , . / {shift}",
|
||||
".com @ {space}"
|
||||
".com @ {space}",
|
||||
],
|
||||
shift: [
|
||||
"~ ! @ # $ % ^ & * ( ) _ + {bksp}",
|
||||
"{tab} Q W E R T Y U I O P { } |",
|
||||
'{lock} A S D F G H J K L : " {enter}',
|
||||
"{shift} Z X C V B N M < > ? {shift}",
|
||||
".com @ {space}"
|
||||
]
|
||||
".com @ {space}",
|
||||
],
|
||||
};
|
||||
};
|
@ -1,13 +1,17 @@
|
||||
import { KeyboardOptions, UtilitiesParams } from "../interfaces";
|
||||
import Utilities from "../services/Utilities";
|
||||
|
||||
/**
|
||||
* Physical Keyboard Service
|
||||
*/
|
||||
class PhysicalKeyboard {
|
||||
getOptions: () => KeyboardOptions;
|
||||
dispatch: any;
|
||||
|
||||
/**
|
||||
* Creates an instance of the PhysicalKeyboard service
|
||||
*/
|
||||
constructor({ dispatch, getOptions }) {
|
||||
constructor({ dispatch, getOptions }: Partial<UtilitiesParams>) {
|
||||
/**
|
||||
* @type {object} A simple-keyboard instance
|
||||
*/
|
||||
@ -20,11 +24,11 @@ class PhysicalKeyboard {
|
||||
Utilities.bindMethods(PhysicalKeyboard, this);
|
||||
}
|
||||
|
||||
handleHighlightKeyDown(event) {
|
||||
handleHighlightKeyDown(event: KeyboardEvent) {
|
||||
const options = this.getOptions();
|
||||
const buttonPressed = this.getSimpleKeyboardLayoutKey(event);
|
||||
|
||||
this.dispatch(instance => {
|
||||
this.dispatch((instance: any) => {
|
||||
const buttonDOM =
|
||||
instance.getButtonElement(buttonPressed) ||
|
||||
instance.getButtonElement(`{${buttonPressed}}`);
|
||||
@ -50,11 +54,11 @@ class PhysicalKeyboard {
|
||||
});
|
||||
}
|
||||
|
||||
handleHighlightKeyUp(event) {
|
||||
handleHighlightKeyUp(event: KeyboardEvent) {
|
||||
const options = this.getOptions();
|
||||
const buttonPressed = this.getSimpleKeyboardLayoutKey(event);
|
||||
|
||||
this.dispatch(instance => {
|
||||
this.dispatch((instance: any) => {
|
||||
const buttonDOM =
|
||||
instance.getButtonElement(buttonPressed) ||
|
||||
instance.getButtonElement(`{${buttonPressed}}`);
|
||||
@ -81,7 +85,7 @@ class PhysicalKeyboard {
|
||||
* Transforms a KeyboardEvent's "key.code" string into a simple-keyboard layout format
|
||||
* @param {object} event The KeyboardEvent
|
||||
*/
|
||||
getSimpleKeyboardLayoutKey(event) {
|
||||
getSimpleKeyboardLayoutKey(event: KeyboardEvent) {
|
||||
let output;
|
||||
|
||||
if (
|
@ -1,11 +1,25 @@
|
||||
import { KeyboardInput } from "./../interfaces";
|
||||
import { KeyboardOptions, UtilitiesParams } from "../interfaces";
|
||||
|
||||
/**
|
||||
* Utility Service
|
||||
*/
|
||||
class Utilities {
|
||||
getOptions: () => KeyboardOptions;
|
||||
getCaretPosition: () => number;
|
||||
getCaretPositionEnd: () => number;
|
||||
dispatch: any;
|
||||
maxLengthReached: boolean;
|
||||
|
||||
/**
|
||||
* Creates an instance of the Utility service
|
||||
*/
|
||||
constructor({ getOptions, getCaretPosition, getCaretPositionEnd, dispatch }) {
|
||||
constructor({
|
||||
getOptions,
|
||||
getCaretPosition,
|
||||
getCaretPositionEnd,
|
||||
dispatch,
|
||||
}: UtilitiesParams) {
|
||||
this.getOptions = getOptions;
|
||||
this.getCaretPosition = getCaretPosition;
|
||||
this.getCaretPositionEnd = getCaretPositionEnd;
|
||||
@ -23,7 +37,7 @@ class Utilities {
|
||||
* @param {string} button The button's layout name
|
||||
* @return {string} The classes to be added to the button
|
||||
*/
|
||||
getButtonClass(button) {
|
||||
getButtonClass(button: string) {
|
||||
const buttonTypeClass =
|
||||
button.includes("{") && button.includes("}") && button !== "{//}"
|
||||
? "functionBtn"
|
||||
@ -100,7 +114,7 @@ class Utilities {
|
||||
"{numpad6}": "6",
|
||||
"{numpad7}": "7",
|
||||
"{numpad8}": "8",
|
||||
"{numpad9}": "9"
|
||||
"{numpad9}": "9",
|
||||
};
|
||||
}
|
||||
/**
|
||||
@ -110,7 +124,11 @@ class Utilities {
|
||||
* @param {object} display The provided display option
|
||||
* @param {boolean} mergeDisplay Whether the provided param value should be merged with the default one.
|
||||
*/
|
||||
getButtonDisplayName(button, display, mergeDisplay) {
|
||||
getButtonDisplayName(
|
||||
button: string,
|
||||
display: KeyboardOptions["display"],
|
||||
mergeDisplay: boolean
|
||||
) {
|
||||
if (mergeDisplay) {
|
||||
display = Object.assign({}, this.getDefaultDiplay(), display);
|
||||
} else {
|
||||
@ -130,14 +148,18 @@ class Utilities {
|
||||
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
|
||||
*/
|
||||
getUpdatedInput(
|
||||
button,
|
||||
input,
|
||||
caretPos,
|
||||
button: string,
|
||||
input: string,
|
||||
caretPos: number,
|
||||
caretPosEnd = caretPos,
|
||||
moveCaret = false
|
||||
) {
|
||||
const options = this.getOptions();
|
||||
const commonParams = [caretPos, caretPosEnd, moveCaret];
|
||||
const commonParams: [number, number, boolean] = [
|
||||
caretPos,
|
||||
caretPosEnd,
|
||||
moveCaret,
|
||||
];
|
||||
|
||||
let output = input;
|
||||
|
||||
@ -194,10 +216,10 @@ class Utilities {
|
||||
* @param {number} length Represents by how many characters the input should be moved
|
||||
* @param {boolean} minus Whether the cursor should be moved to the left or not.
|
||||
*/
|
||||
updateCaretPos(length, minus) {
|
||||
updateCaretPos(length: number, minus = false) {
|
||||
const newCaretPos = this.updateCaretPosAction(length, minus);
|
||||
|
||||
this.dispatch(instance => {
|
||||
this.dispatch((instance: any) => {
|
||||
instance.setCaretPosition(newCaretPos);
|
||||
});
|
||||
}
|
||||
@ -208,7 +230,7 @@ class Utilities {
|
||||
* @param {number} length Represents by how many characters the input should be moved
|
||||
* @param {boolean} minus Whether the cursor should be moved to the left or not.
|
||||
*/
|
||||
updateCaretPosAction(length, minus) {
|
||||
updateCaretPosAction(length: number, minus = false) {
|
||||
const options = this.getOptions();
|
||||
let caretPosition = this.getCaretPosition();
|
||||
|
||||
@ -219,7 +241,7 @@ class Utilities {
|
||||
}
|
||||
|
||||
if (options.debug) {
|
||||
console.log("Caret at:", caretPosition, `(${this.keyboardDOMClass})`);
|
||||
console.log("Caret at:", caretPosition);
|
||||
}
|
||||
|
||||
return caretPosition;
|
||||
@ -234,8 +256,8 @@ class Utilities {
|
||||
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
|
||||
*/
|
||||
addStringAt(
|
||||
source,
|
||||
str,
|
||||
source: string,
|
||||
str: string,
|
||||
position = source.length,
|
||||
positionEnd = source.length,
|
||||
moveCaret = false
|
||||
@ -268,7 +290,7 @@ class Utilities {
|
||||
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
|
||||
*/
|
||||
removeAt(
|
||||
source,
|
||||
source: string,
|
||||
position = source.length,
|
||||
positionEnd = source.length,
|
||||
moveCaret = false
|
||||
@ -314,7 +336,7 @@ class Utilities {
|
||||
} else {
|
||||
output = source.slice(0, position) + source.slice(positionEnd);
|
||||
if (moveCaret) {
|
||||
this.dispatch(instance => {
|
||||
this.dispatch((instance: any) => {
|
||||
instance.setCaretPosition(position);
|
||||
});
|
||||
}
|
||||
@ -328,7 +350,7 @@ class Utilities {
|
||||
* @param {object} inputObj
|
||||
* @param {string} updatedInput
|
||||
*/
|
||||
handleMaxLength(inputObj, updatedInput) {
|
||||
handleMaxLength(inputObj: KeyboardInput, updatedInput: string) {
|
||||
const options = this.getOptions();
|
||||
const maxLength = options.maxLength;
|
||||
const currentInput = inputObj[options.inputName];
|
||||
@ -403,7 +425,7 @@ class Utilities {
|
||||
* Bind all methods in a given class
|
||||
*/
|
||||
|
||||
static bindMethods(myClass, instance) {
|
||||
static bindMethods(myClass: any, instance: any) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
for (const myMethod of Object.getOwnPropertyNames(myClass.prototype)) {
|
||||
const excludeMethod =
|
||||
@ -419,8 +441,8 @@ class Utilities {
|
||||
*
|
||||
* @param {string} str The string to transform.
|
||||
*/
|
||||
camelCase(str) {
|
||||
if (!str) return false;
|
||||
camelCase(str: string): string {
|
||||
if (!str) return;
|
||||
|
||||
return str
|
||||
.toLowerCase()
|
@ -1,5 +0,0 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom/extend-expect';
|
17
tsconfig.json
Normal file
17
tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true,
|
||||
"outFile": "build/index.d.ts",
|
||||
"module": "esnext",
|
||||
"target": "es5",
|
||||
"allowJs": true,
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"emitDeclarationOnly": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"lib": ["es2018", "dom"],
|
||||
"moduleResolution": "node",
|
||||
},
|
||||
"include": ["src/lib"],
|
||||
"exclude": ["src/**/tests"],
|
||||
}
|
90
webpack.config.js
Normal file
90
webpack.config.js
Normal file
@ -0,0 +1,90 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const PrettierPlugin = require("prettier-webpack-plugin");
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
const getPackageJson = require('./scripts/getPackageJson');
|
||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
|
||||
|
||||
const {
|
||||
version,
|
||||
name,
|
||||
license,
|
||||
repository,
|
||||
author,
|
||||
} = getPackageJson('version', 'name', 'license', 'repository', 'author');
|
||||
|
||||
const banner = `
|
||||
${name} v${version}
|
||||
${repository.url}
|
||||
|
||||
Copyright (c) ${author.replace(/ *<[^)]*> */g, " ")} and project contributors.
|
||||
|
||||
This source code is licensed under the ${license} license found in the
|
||||
LICENSE file in the root directory of this source tree.
|
||||
`;
|
||||
|
||||
module.exports = {
|
||||
mode: "production",
|
||||
devtool: 'source-map',
|
||||
entry: './src/lib/index.ts',
|
||||
output: {
|
||||
filename: 'index.js',
|
||||
path: path.resolve(__dirname, 'build'),
|
||||
library: "SimpleKeyboard",
|
||||
libraryTarget: 'umd',
|
||||
clean: true
|
||||
},
|
||||
optimization: {
|
||||
minimize: true,
|
||||
minimizer: [
|
||||
new TerserPlugin({ extractComments: false }),
|
||||
new OptimizeCSSAssetsPlugin({
|
||||
cssProcessorOptions: {
|
||||
map: {
|
||||
inline: false
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
open: true,
|
||||
hot: true,
|
||||
host: "localhost",
|
||||
static: path.join(__dirname, 'demo'),
|
||||
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 PrettierPlugin(),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: 'css/index.css'
|
||||
}),
|
||||
new webpack.BannerPlugin(banner)
|
||||
],
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js', '.json']
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user