Compare commits

...
146 Commits
Author SHA1 Message Date
Francisco Hodge 74c3bc692f Build update 2019-03-06 20:22:18 -05:00
Francisco Hodge ae1436a52d Reinstating method bindings 2019-03-06 20:22:09 -05:00
Francisco Hodge adc1db7a7f Repairing CI, per node bug https://github.com/facebook/jest/issues/8069 2019-03-06 20:13:32 -05:00
Francisco Hodge ec983bf82b Build update 2019-03-06 20:00:48 -05:00
Francisco Hodge 0d82696868 Tests update 2019-03-06 19:45:08 -05:00
Francisco Hodge 477972f26f Build update 2019-03-06 19:37:49 -05:00
Francisco Hodge 18a3aa9dd5 Added methods beforeRender, beforeFirstRender 2019-03-06 19:33:28 -05:00
Francisco Hodge c007f7406a Added enabling isMouseHold for delete and backspace buttons 2019-03-06 19:32:46 -05:00
Francisco Hodge 1a53fae5d6 Added isTouchDevice and touch device helper methods 2019-03-06 19:32:03 -05:00
Francisco Hodge 00f8daff3c Added binding of methods 2019-03-06 19:31:07 -05:00
Francisco Hodge abacee54ee Updated tests 2019-03-06 19:30:06 -05:00
Francisco Hodge 93a45a7c26 Added internal getButtonTheme 2019-03-06 19:29:29 -05:00
Francisco Hodge c4005e7409 Updated types 2019-03-06 19:28:24 -05:00
Francisco Hodge ed944d1204 Added autoUseTouchEvents and related touch device fixes 2019-03-06 19:28:12 -05:00
Francisco Hodge 30042d5d72 Disable pointer events for .hg-button span elements 2019-03-06 19:25:55 -05:00
Francisco Hodge c97945c8b1 Updated dev dependencies 2019-03-02 18:07:43 -05:00
Francisco Hodge bee709e3b5 Remove max-width for standard buttons 2019-02-25 20:26:30 -05:00
Francisco Hodge 92d1e822a5 Updated dev dependencies 2019-02-23 14:44:13 -05:00
Francisco Hodge 3700bbe144 MIT Licensing 2019-02-23 14:44:05 -05:00
Francisco Hodge 4e4c187cea Updated dev dependencies 2019-02-20 18:17:42 -05:00
Francisco Hodge 974eaf8f5c Updated dev dependencies 2019-02-16 11:12:38 -05:00
Francisco Hodge 70d2cca7ac Adding PrettierPlugin to build process 2019-02-15 21:49:27 -05:00
Francisco Hodge 05976e0e7b Adding unminified bundle to dist 2019-02-15 21:48:40 -05:00
Francisco Hodge 4a35dff92f Updated dev dependencies 2019-02-09 10:19:48 -05:00
Francisco Hodge 04def5da87 License: Supporting both MIT and GPLv3 2019-01-28 11:11:28 -05:00
Francisco Hodge dc085b377d Updated dev dependencies 2019-01-27 20:02:29 -05:00
Francisco Hodge effee00b87 Added new inputPattern option 2019-01-24 23:04:59 -05:00
Francisco Hodge 90b408eff6 Tests update 2019-01-24 23:03:48 -05:00
Francisco Hodge 21fa732b1f Types update 2019-01-24 23:03:23 -05:00
Francisco Hodge 80f4307c36 Build update 2019-01-24 23:03:14 -05:00
Francisco Hodge 681bb3d0ee npm update 2019-01-24 23:02:59 -05:00
Francisco Hodge e1fa685c6c Merge branch 'master' of https://github.com/hodgef/simple-keyboard 2019-01-17 18:37:07 -05:00
Francisco Hodge 2de7154128 npm update 2019-01-17 18:36:48 -05:00
Francisco Hodge c9afe43f5b Build update 2019-01-17 18:36:36 -05:00
Francisco Hodge c59f819db9 Tests update 2019-01-17 18:36:19 -05:00
Francisco Hodge 1f9efb0c66 Ensure caretPosition is unset on disableCaretPositioning 2019-01-17 18:36:09 -05:00
Francisco HodgeandGitHub 32cbce8c8a Update issue templates 2019-01-17 09:20:47 -05:00
Francisco Hodge 6c2236f762 Adding types for disableCaretPositioning option 2019-01-16 19:38:42 -05:00
Francisco Hodge 23a50e6f44 Updated devDependencies 2019-01-06 15:44:07 -05:00
Francisco Hodge c316d493c7 Tests update 2018-12-27 13:46:15 -05:00
Francisco Hodge 70e7635f25 npm update 2018-12-27 13:38:38 -05:00
Francisco HodgeandGitHub f5b73b09e7 Merge pull request #45 from mkusher/patch-1
fixed camelCase function
2018-12-27 13:33:09 -05:00
Aleh KashnikauandGitHub 7432e01740 fixed camelCase function
Added check for empty word
2018-12-27 21:29:05 +03:00
Francisco Hodge 459ab71c45 npm update 2018-12-24 13:31:13 -05:00
Francisco Hodge a5c177e41c Specifying layout class on keyboard element 2018-12-24 13:30:52 -05:00
Francisco Hodge d382945de7 Updated devDependencies 2018-12-23 17:42:02 -05:00
Francisco Hodge 4335f477a1 Adding new option: useButtonTag 2018-12-14 00:28:18 -05:00
Francisco Hodge d03f00c0d1 Updated devDependencies 2018-12-08 15:49:33 -05:00
Francisco HodgeandGitHub 9cea07dd41 Merge pull request #41 from armno/fix-incorrect-return-type-definition
[Angular] Fix return type of getButtonElement()
2018-12-04 20:11:10 -05:00
Francisco Hodge efe18e7240 Build update 2018-12-04 20:06:52 -05:00
Francisco Hodge 82e242a806 Modified getInput return type 2018-12-04 20:06:32 -05:00
Armno 4a9d485d5b add return type for getButtonElement in type definition file 2018-12-04 10:42:05 +07:00
Francisco Hodge 6ffd2e77ad Improved Angular types support 2018-12-02 14:48:46 -05:00
Francisco Hodge 7753bc5ba3 Build update 2018-11-28 09:04:10 -05:00
Francisco HodgeandGitHub 9c4fa00363 Merge pull request #40 from armno/master
Add type definition file for TypeScript
2018-11-28 08:58:36 -05:00
Francisco Hodge 251af057d8 npm update 2018-11-28 08:53:06 -05:00
Francisco Hodge e60574d56d Build update 2018-11-28 08:52:58 -05:00
Francisco Hodge b3b482e864 Copy index.d.ts to build folder on build 2018-11-28 08:52:27 -05:00
Armno ff037ad5b7 remove exporting KeyboardOptions interface 2018-11-28 12:43:13 +07:00
Armno ede2ce04b1 add overloaded constructor type definition 2018-11-28 11:56:00 +07:00
Armno 10380099e6 add more types 2018-11-28 11:20:32 +07:00
Armno 058b6774da Merge remote-tracking branch 'upstream/master' 2018-11-28 09:17:25 +07:00
Francisco Hodge 3b12b278f7 build update 2018-11-27 18:00:24 -05:00
Francisco Hodge 3523df1ef5 npm update 2018-11-27 17:55:06 -05:00
Francisco HodgeandGitHub 178c63cf98 Update README.md 2018-11-27 17:49:12 -05:00
Francisco HodgeandGitHub ec452963fb Update README.md 2018-11-27 17:40:17 -05:00
Armno b5cbfa780b add public methods type definitions 2018-11-27 23:43:20 +07:00
Armno 5c40dd3109 WIP: create type definition file 2018-11-27 19:57:08 +07:00
Francisco Hodge 7ab6baa0f4 Build update 2018-11-25 21:48:14 -05:00
Francisco Hodge 0512e2c624 Fix full keyboard numpad cursor positioning 2018-11-25 21:48:08 -05:00
Francisco Hodge 2693030203 Readme update 2018-11-25 20:15:15 -05:00
Francisco Hodge 3cbdf604fe Updated dev dependencies 2018-11-25 18:16:27 -05:00
Francisco Hodge d87cb82238 Readme update 2018-11-25 18:16:05 -05:00
Francisco Hodge 125f6b6504 Tests update 2018-11-25 18:11:09 -05:00
Francisco Hodge 97b475a2b7 Setting preventMouseDownDefault false by default 2018-11-25 12:03:40 -05:00
Francisco Hodge 682660d638 Docs update 2018-11-24 21:12:28 -05:00
Francisco Hodge d8c1327eff Bind caret handler events once regardless of number of instances 2018-11-24 21:12:19 -05:00
Francisco Hodge b8fc9425ba Demo test update 2018-11-24 21:10:47 -05:00
Francisco Hodge 5c42ae3fbb Demo update 2018-11-24 21:10:33 -05:00
Francisco Hodge a8e826679f npm update 2018-11-24 21:10:07 -05:00
Francisco Hodge f78bfc0d02 Build update 2018-11-24 21:10:00 -05:00
Francisco Hodge 9c12f410ed Build update 2018-11-24 20:31:26 -05:00
Francisco HodgeandGitHub f83884af5c Merge pull request #39 from SteinRobert/preventDefault-for-mousedown-option
Adds preventDefault to mousedown event
2018-11-24 20:21:23 -05:00
Francisco Hodge be25e26c46 npm update 2018-11-24 20:13:02 -05:00
Francisco Hodge 3b93f37cb2 Docs update 2018-11-24 20:12:52 -05:00
Francisco Hodge 822e7a97b7 Build update 2018-11-24 20:12:04 -05:00
Francisco Hodge bc420bb60c Adding ternary condition for preventMouseDownDefault 2018-11-24 20:11:48 -05:00
Robert Stein 1aedf9c487 - changes mousedown event simulation so that event object is passed
to `onmousedown` handler
2018-11-24 16:43:30 +01:00
Robert Stein 0052efa582 - preventDefault is now called for mousedown event by default
- this can be disabled by setting preventMouseDownDefault to false
2018-11-24 16:29:13 +01:00
Francisco Hodge 316319a311 Tests update 2018-11-19 09:32:04 -05:00
Francisco Hodge 02bc34e3b7 npm update 2018-11-19 09:30:17 -05:00
Francisco Hodge d38433be1c Fix buttonHold issue 2018-11-19 09:29:49 -05:00
Francisco Hodge 366f4cd98e Fix clearInput issue 2018-11-19 09:25:27 -05:00
Francisco Hodge ee3d6a18f2 npm update 2018-11-16 23:31:05 -05:00
Francisco Hodge e6d6c78389 Updating tests 2018-11-16 23:30:57 -05:00
Francisco Hodge ba09ac7952 Updating devDependencies 2018-11-16 23:24:53 -05:00
Francisco Hodge 3ee9873d77 2.9.0 - Adding useTouchEvents 2018-11-16 00:39:49 -05:00
Francisco Hodge 347480735c 2.9.0 2018-11-16 00:32:09 -05:00
Francisco HodgeandGitHub 3086a45295 Merge pull request #35 from MadDeveloper/tactile-touch-events
feat(Keyboard): add touch events (by @MadDeveloper)
2018-11-16 00:26:12 -05:00
Francisco Hodge d33d557f08 Updating github templates 2018-11-15 12:18:24 -05:00
Julien Sergent f8fb18e244 feat(Keyboard): add touch events
Can be enabled passing `useTouchEvents` option to the Keyboard constructor.
2018-11-15 17:05:39 +01:00
Francisco Hodge 2d8799367a Updating har-validator 2018-11-14 14:06:26 -05:00
Francisco Hodge 7698e29373 2.8.0 - Webpack 4, Babel 7 2018-11-09 11:06:46 -05:00
dependabot[bot]andGitHub 9f96f58663 Merge pull request #32 from hodgef/dependabot/npm_and_yarn/dotenv-6.1.0 2018-11-07 20:29:09 +00:00
dependabot[bot]andGitHub 226faaa7ec Merge pull request #31 from hodgef/dependabot/npm_and_yarn/webpack-manifest-plugin-2.0.4 2018-11-07 20:28:54 +00:00
dependabot[bot]andGitHub e0d8bf8824 Merge pull request #33 from hodgef/dependabot/npm_and_yarn/react-dom-16.6.1 2018-11-07 11:05:51 +00:00
dependabot[bot] 26c6c88cae Bump webpack-manifest-plugin from 1.3.2 to 2.0.4
Bumps [webpack-manifest-plugin](https://github.com/danethurber/webpack-manifest-plugin) from 1.3.2 to 2.0.4.
- [Release notes](https://github.com/danethurber/webpack-manifest-plugin/releases)
- [Commits](https://github.com/danethurber/webpack-manifest-plugin/compare/v1.3.2...v2.0.4)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-07 10:55:15 +00:00
dependabot[bot] effb564455 Bump react-dom from 16.6.0 to 16.6.1
Bumps [react-dom](https://github.com/facebook/react) from 16.6.0 to 16.6.1.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/compare/v16.6.0...v16.6.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-07 10:55:10 +00:00
dependabot[bot] 924a30cca8 Bump dotenv from 4.0.0 to 6.1.0
Bumps [dotenv](https://github.com/motdotla/dotenv) from 4.0.0 to 6.1.0.
- [Release notes](https://github.com/motdotla/dotenv/releases)
- [Changelog](https://github.com/motdotla/dotenv/blob/master/CHANGELOG.md)
- [Commits](https://github.com/motdotla/dotenv/compare/v4.0.0...v6.1.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-07 10:53:42 +00:00
dependabot[bot]andGitHub a3c1451c2e Merge pull request #30 from hodgef/dependabot/npm_and_yarn/webpack-3.12.0 2018-11-07 10:52:38 +00:00
dependabot[bot] 2efc9b5469 Bump webpack from 3.8.1 to 3.12.0
Bumps [webpack](https://github.com/webpack/webpack) from 3.8.1 to 3.12.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v3.8.1...v3.12.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-07 10:48:17 +00:00
Francisco Hodge 3b13360fc1 npm update 2018-11-06 17:45:19 -05:00
dependabot[bot]andGitHub a18836f95d Merge pull request #26 from hodgef/dependabot/npm_and_yarn/eslint-loader-2.1.1 2018-11-06 19:54:19 +00:00
dependabot[bot]andGitHub 9a9a234761 Merge pull request #25 from hodgef/dependabot/npm_and_yarn/postcss-flexbugs-fixes-4.1.0 2018-11-06 19:53:18 +00:00
dependabot[bot]andGitHub 577553299b Merge pull request #28 from hodgef/dependabot/npm_and_yarn/react-16.6.0 2018-11-06 10:42:05 +00:00
dependabot[bot] f62dc0f87a Bump eslint-loader from 1.9.0 to 2.1.1
Bumps [eslint-loader](https://github.com/webpack-contrib/eslint-loader) from 1.9.0 to 2.1.1.
- [Release notes](https://github.com/webpack-contrib/eslint-loader/releases)
- [Changelog](https://github.com/webpack-contrib/eslint-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/eslint-loader/compare/1.9.0...2.1.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-06 10:36:40 +00:00
dependabot[bot] f6b16099af Bump react from 16.2.0 to 16.6.0
Bumps [react](https://github.com/facebook/react) from 16.2.0 to 16.6.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/compare/v16.2.0...v16.6.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-06 10:36:36 +00:00
dependabot[bot]andGitHub b03a90b7aa Merge pull request #27 from hodgef/dependabot/npm_and_yarn/eslint-plugin-flowtype-2.50.3 2018-11-06 10:34:21 +00:00
dependabot[bot] eebdd570e1 Bump eslint-plugin-flowtype from 2.39.1 to 2.50.3
Bumps [eslint-plugin-flowtype](https://github.com/gajus/eslint-plugin-flowtype) from 2.39.1 to 2.50.3.
- [Release notes](https://github.com/gajus/eslint-plugin-flowtype/releases)
- [Commits](https://github.com/gajus/eslint-plugin-flowtype/compare/v2.39.1...v2.50.3)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-06 10:29:05 +00:00
dependabot[bot] 32457f8471 Bump postcss-flexbugs-fixes from 3.2.0 to 4.1.0
Bumps [postcss-flexbugs-fixes](https://github.com/luisrudge/postcss-flexbugs-fixes) from 3.2.0 to 4.1.0.
- [Release notes](https://github.com/luisrudge/postcss-flexbugs-fixes/releases)
- [Changelog](https://github.com/luisrudge/postcss-flexbugs-fixes/blob/master/CHANGELOG.md)
- [Commits](https://github.com/luisrudge/postcss-flexbugs-fixes/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-06 10:23:21 +00:00
Francisco Hodge 2177211841 Updating devDependencies 2018-11-05 18:31:04 -05:00
dependabot[bot]andGitHub 251260a555 Merge pull request #23 from hodgef/dependabot/npm_and_yarn/css-loader-0.28.11 2018-11-05 23:19:22 +00:00
dependabot[bot]andGitHub 00286710b2 Merge pull request #21 from hodgef/dependabot/npm_and_yarn/style-loader-0.23.1 2018-11-05 23:14:01 +00:00
dependabot[bot] 95f71d31b1 Bump css-loader from 0.28.7 to 0.28.11
Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 0.28.7 to 0.28.11.
- [Release notes](https://github.com/webpack-contrib/css-loader/releases)
- [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/css-loader/compare/v0.28.7...v0.28.11)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-05 22:43:50 +00:00
dependabot[bot] 66479fdfaa Bump style-loader from 0.19.0 to 0.23.1
Bumps [style-loader](https://github.com/webpack-contrib/style-loader) from 0.19.0 to 0.23.1.
- [Release notes](https://github.com/webpack-contrib/style-loader/releases)
- [Changelog](https://github.com/webpack-contrib/style-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/style-loader/compare/v0.19.0...v0.23.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-05 22:40:12 +00:00
Francisco Hodge 4e3a06fd78 npm update 2018-11-05 17:36:50 -05:00
Francisco Hodge b89f09ea13 Merge branch 'master' of https://github.com/hodgef/simple-keyboard 2018-11-05 17:34:48 -05:00
Francisco Hodge d1100cef92 Fixing caret issue when using maxLength 2018-11-05 17:34:16 -05:00
Francisco Hodge 6475dcacf2 Build update 2018-11-05 17:31:41 -05:00
Francisco Hodge 4e5d36173d Docs update 2018-11-05 17:31:11 -05:00
dependabot[bot]andGitHub 931bb2a7c9 Merge pull request #20 from hodgef/dependabot/npm_and_yarn/copy-webpack-plugin-4.6.0 2018-11-05 10:53:47 +00:00
dependabot[bot] 99c213e41b Bump copy-webpack-plugin from 4.5.0 to 4.6.0
Bumps [copy-webpack-plugin](https://github.com/webpack-contrib/copy-webpack-plugin) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/webpack-contrib/copy-webpack-plugin/releases)
- [Changelog](https://github.com/webpack-contrib/copy-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.5.0...v4.6.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-05 10:51:45 +00:00
Francisco Hodge 221c41933b Merge branch 'master' of https://github.com/hodgef/simple-keyboard 2018-11-04 20:46:10 -05:00
Francisco Hodge 3a4cf5faaa Build update 2018-11-04 20:45:43 -05:00
dependabot[bot]andGitHub 708b97d64e Merge pull request #19 from hodgef/dependabot/npm_and_yarn/eslint-config-react-app-2.1.0 2018-11-05 01:38:13 +00:00
dependabot[bot]andGitHub b41c489803 Merge pull request #18 from hodgef/dependabot/npm_and_yarn/autoprefixer-9.3.1 2018-11-05 01:34:14 +00:00
dependabot[bot] 2c78c56f5e Bump autoprefixer from 7.1.6 to 9.3.1
Bumps [autoprefixer](https://github.com/postcss/autoprefixer) from 7.1.6 to 9.3.1.
- [Release notes](https://github.com/postcss/autoprefixer/releases)
- [Changelog](https://github.com/postcss/autoprefixer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/postcss/autoprefixer/compare/7.1.6...9.3.1)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-05 01:27:15 +00:00
dependabot[bot]andGitHub 5fe8a198f1 Merge pull request #17 from hodgef/dependabot/npm_and_yarn/sw-precache-webpack-plugin-0.11.5 2018-11-05 01:24:31 +00:00
dependabot[bot]andGitHub e9c3b280ac Merge pull request #16 from hodgef/dependabot/npm_and_yarn/react-dom-16.6.0 2018-11-05 01:18:48 +00:00
dependabot[bot] 3a97f43b5d Bump eslint-config-react-app from 2.0.1 to 2.1.0
Bumps [eslint-config-react-app](https://github.com/facebook/create-react-app) from 2.0.1 to 2.1.0.
- [Release notes](https://github.com/facebook/create-react-app/releases)
- [Changelog](https://github.com/facebook/create-react-app/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/create-react-app/compare/react-scripts@2.0.1...v2.1.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-05 00:37:40 +00:00
dependabot[bot] 84ab70cdb2 Bump sw-precache-webpack-plugin from 0.11.4 to 0.11.5
Bumps [sw-precache-webpack-plugin](https://github.com/goldhand/sw-precache-webpack-plugin) from 0.11.4 to 0.11.5.
- [Release notes](https://github.com/goldhand/sw-precache-webpack-plugin/releases)
- [Changelog](https://github.com/goldhand/sw-precache-webpack-plugin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/goldhand/sw-precache-webpack-plugin/compare/v0.11.4...v0.11.5)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-05 00:33:21 +00:00
dependabot[bot] ba93b6f648 Bump react-dom from 16.2.0 to 16.6.0
Bumps [react-dom](https://github.com/facebook/react) from 16.2.0 to 16.6.0.
- [Release notes](https://github.com/facebook/react/releases)
- [Changelog](https://github.com/facebook/react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/react/compare/v16.2.0...v16.6.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-05 00:31:49 +00:00
Francisco Hodge 9abb36172d Adding disableCaretPositioning option 2018-11-04 17:25:39 -05:00
Francisco Hodge 701db33ab1 Merge branch 'master' of https://github.com/hodgef/simple-keyboard 2018-11-03 01:41:10 -04:00
Francisco Hodge 2eb88579ca Button hold adjustment 2018-11-03 01:39:41 -04:00
Francisco Hodge a309fd6cb4 Update issue templates 2018-11-01 19:53:36 -04:00
57 changed files with 119430 additions and 88997 deletions
+5 -5
View File
@@ -1,17 +1,17 @@
--- ---
name: Bug report name: Bug report
about: Create a report to help improve simple-keyboard about: Create a report to help improve simple-keyboard
title: ''
labels: ''
assignees: hodgef
--- ---
** simple-keyboard version you are using ** **Simple-keyboard version**
As some bugs have been addressed in later versions, please ensure you are running the latest. As some bugs have been addressed in later versions, please ensure you are running the latest.
**Describe the bug** **Describe the bug**
A clear and concise description of what the bug is. A clear and concise description of what the bug is. Providing a [sandbox example](https://codesandbox.io/s/vanilla) or code depicting the issue is important, as this will help us reproduce the issue.
**Create a sandbox example**
Edit [this sandbox example](https://codesandbox.io/s/krzkx19rr) and provide the new link.
**Screenshots** **Screenshots**
If applicable, add screenshots to help explain your problem. If applicable, add screenshots to help explain your problem.
@@ -1,6 +1,9 @@
--- ---
name: Feature request name: Feature request
about: Suggest an idea you'd like to see in simple-keyboard about: Suggest an idea you'd like to see in simple-keyboard
title: ''
labels: ''
assignees: ''
--- ---
+8
View File
@@ -0,0 +1,8 @@
## Description
A few sentences describing the overall goals of the pull request's commits.
## Checks
- [ ] Tests ( `npm run test -- --coverage` ) Coverage at `./coverage/lcov-report/index.html` should be 100%
- [ ] Documentation ( `npm run docs` ) Coverage at `./docs/source.html` should be 100%
+3 -1
View File
@@ -1,7 +1,9 @@
# See https://help.github.com/ignore-files/ for more about ignoring files. # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies # dependencies
/node_modules /node_modules
/.pnp
.pnp.js
# production # production
/demo /demo
+2 -1
View File
@@ -1,11 +1,12 @@
language: node_js language: node_js
node_js: node_js:
- "node" - '11.10.1'
install: install:
- npm install -g codecov - npm install -g codecov
- npm install - npm install
script: script:
- npm run test -- --coverage - npm run test -- --coverage
- npm run demo
- codecov - codecov
after_success: after_success:
- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh - wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh
+2 -2
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2018 Francisco Hodge Copyright (c) 2019 Francisco Hodge
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@@ -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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
+6 -3
View File
@@ -1,12 +1,13 @@
<div align="center"> <div align="center">
<a href="https://simple-keyboard.com/demo" title="View Demo" target="_blank"><img src="https://franciscohodge.com/project-pages/simple-keyboard/images/simplekeyboard-banner_B.png" align="center" width="100%"></a> <a href="https://simple-keyboard.com/demo" title="View Demo" target="_blank"><img src="https://franciscohodge.com/project-pages/simple-keyboard/images/simplekeyboard-banner_B.png" align="center" width="100%"></a>
<a href="https://simple-keyboard.com/demo" title="View Demo" target="_blank"><img src="https://franciscohodge.com/project-pages/simple-keyboard/images/simple-keyboard-10172018.gif" align="center" width="100%"></a> <a href="https://simple-keyboard.com/demo" title="View Demo" target="_blank"><img src="https://franciscohodge.com/project-pages/simple-keyboard/images/skdemo-4x2.gif" align="center" width="100%"></a>
<blockquote>The easily customisable and responsive on-screen virtual keyboard for Javascript projects.</blockquote> <blockquote>The easily customisable and responsive on-screen virtual keyboard for Javascript projects.</blockquote>
<p><a href="https://github.com/hodgef/simple-keyboard/blob/master/LICENSE" target="_blank"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License"></a>
<a href="https://www.npmjs.com/package/simple-keyboard" target="_blank"><img src="https://img.shields.io/npm/v/simple-keyboard.svg?style=flat" alt="npm version"></a> <a href="https://www.npmjs.com/package/simple-keyboard" target="_blank"><img src="https://img.shields.io/npm/v/simple-keyboard.svg?style=flat" alt="npm version"></a>
<a href="https://travis-ci.org/hodgef/simple-keyboard" target="_blank"><img src="https://travis-ci.org/hodgef/simple-keyboard.svg?branch=master" alt="Build Status"></a> <a href="https://travis-ci.org/hodgef/simple-keyboard" target="_blank"><img src="https://travis-ci.org/hodgef/simple-keyboard.svg?branch=master" alt="Build Status"></a>
<a href="https://codecov.io/gh/hodgef/simple-keyboard" target="_blank"><img src="https://img.shields.io/codecov/c/github/hodgef/simple-keyboard/master.svg?style=flat" alt="Coverage Status"></a> <a href="https://codecov.io/gh/hodgef/simple-keyboard" target="_blank"><img src="https://img.shields.io/codecov/c/github/hodgef/simple-keyboard/master.svg?style=flat" alt="Coverage Status"></a>
<a href="https://doc.esdoc.org/github.com/hodgef/simple-keyboard" target="_blank"><img src="https://doc.esdoc.org/github.com/hodgef/simple-keyboard/badge.svg" alt="Documentation Status"></a> <a href="https://doc.esdoc.org/github.com/hodgef/simple-keyboard" target="_blank"><img src="https://doc.esdoc.org/github.com/hodgef/simple-keyboard/badge.svg" alt="Documentation Status"></a>
<img src="https://img.shields.io/david/dev/hodgef/simple-keyboard.svg" alt="Dev dependencies">
<a href="https://www.codacy.com/app/hodgef/simple-keyboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=hodgef/simple-keyboard&amp;utm_campaign=Badge_Grade" target="_blank"><img src="https://api.codacy.com/project/badge/Grade/5778fccc6a894701853d9a1f2fb44a76" alt="Codacy Badge"></a>
</p> </p>
</div> </div>
@@ -34,7 +35,9 @@ Feel free to browse the [Q&A / Use-cases](https://simple-keyboard.com/qa-use-cas
### Other versions ### Other versions
* ReactJS - [react-simple-keyboard](https://github.com/hodgef/react-simple-keyboard) * [React.js](https://github.com/hodgef/react-simple-keyboard)
* [Angular](https://simple-keyboard.com/demo)
* [Vue.js](https://simple-keyboard.com/demo)
### Questions? ### Questions?
+3 -4
View File
@@ -1,6 +1,6 @@
/*! /*!
* *
* simple-keyboard v2.7.7 * simple-keyboard v2.16.0
* https://github.com/hodgef/simple-keyboard * https://github.com/hodgef/simple-keyboard
* *
* Copyright (c) Francisco Hodge (https://github.com/hodgef) * Copyright (c) Francisco Hodge (https://github.com/hodgef)
@@ -8,6 +8,5 @@
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
* *
*/ */body,html{margin:0;padding:0}.simple-keyboard{font-family:HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif;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}.simple-keyboard .hg-row{display:-webkit-flex;display:flex}.simple-keyboard .hg-row:not(:last-child){margin-bottom:5px}.simple-keyboard .hg-row .hg-button:not(:last-child){margin-right:5px}.simple-keyboard .hg-button{display:inline-block;-webkit-flex-grow:1;flex-grow:1;cursor:pointer}.simple-keyboard .hg-button span{pointer-events:none}.simple-keyboard.hg-theme-default{background-color:rgba(0,0,0,.1);padding:5px;border-radius:5px}.simple-keyboard.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:-webkit-flex;display:flex;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center}.simple-keyboard button.hg-button{border-width:0;outline:0;font-size:inherit}.simple-keyboard.hg-theme-default:not(.hg-touch-events) .hg-button:active{background:#e4e4e4}.simple-keyboard.hg-theme-default.hg-layout-numeric .hg-button{width:33.3%;height:60px;-webkit-align-items:center;align-items:center;display:-webkit-flex;display:flex;-webkit-justify-content:center;justify-content:center}.simple-keyboard.hg-theme-default .hg-button.hg-button-numpadadd,.simple-keyboard.hg-theme-default .hg-button.hg-button-numpadenter{height:85px}.simple-keyboard.hg-theme-default .hg-button.hg-button-numpad0{width:105px}.simple-keyboard.hg-theme-default .hg-button.hg-button-com{max-width:85px}.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn.hg-button-at{max-width:45px}.simple-keyboard.hg-theme-default .hg-button.hg-selectedButton{background:rgba(5,25,70,.53);color:#fff}.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn[data-skbtn=".com"]{max-width:82px}.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn[data-skbtn="@"]{max-width:60px}
body,html{margin:0;padding:0}.simple-keyboard{font-family:HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif;width:100%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-ms-touch-action:manipulation;touch-action:manipulation}.simple-keyboard .hg-row{display:-ms-flexbox;display:flex}.simple-keyboard .hg-row:not(:last-child){margin-bottom:5px}.simple-keyboard .hg-row .hg-button:not(:last-child){margin-right:5px}.simple-keyboard .hg-button{display:inline-block;-ms-flex-positive:1;flex-grow:1;cursor:pointer}.hg-standardBtn{max-width:100px}.simple-keyboard.hg-theme-default{background-color:rgba(0,0,0,.1);padding:5px;border-radius:5px}.simple-keyboard.hg-theme-default .hg-button{-webkit-box-shadow:0 0 3px -1px rgba(0,0,0,.3);box-shadow:0 0 3px -1px rgba(0,0,0,.3);height:40px;border-radius:5px;-webkit-box-sizing:border-box;box-sizing:border-box;padding:5px;background:#fff;border-bottom:1px solid #b5b5b5;cursor:pointer;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.simple-keyboard.hg-theme-default .hg-button:active{background:#e4e4e4}.simple-keyboard.hg-theme-default.hg-layout-numeric .hg-button{width:33.3%;height:60px;-ms-flex-align:center;align-items:center;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center}.simple-keyboard.hg-theme-default .hg-button.hg-button-numpadadd,.simple-keyboard.hg-theme-default .hg-button.hg-button-numpadenter{height:85px}.simple-keyboard.hg-theme-default .hg-button.hg-button-numpad0{width:105px}.simple-keyboard.hg-theme-default .hg-button.hg-button-com{max-width:85px}.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn.hg-button-at{max-width:45px}.simple-keyboard.hg-theme-default .hg-button.hg-selectedButton{background:rgba(5,25,70,.53);color:#fff}.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn[data-skbtn=".com"]{max-width:82px}.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn[data-skbtn="@"]{max-width:60px} /*# sourceMappingURL=index.css.map */
/*# sourceMappingURL=index.css.map*/
File diff suppressed because one or more lines are too long
+135
View File
@@ -0,0 +1,135 @@
/*!
*
* simple-keyboard v2.16.0 (Non-minified build)
* https://github.com/hodgef/simple-keyboard
*
* Copyright (c) Francisco Hodge (https://github.com/hodgef)
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
body,
html {
margin: 0;
padding: 0;
}
.simple-keyboard {
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue",
Helvetica, Arial, "Lucida Grande", sans-serif;
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;
}
.simple-keyboard .hg-row {
display: -webkit-flex;
display: flex;
}
.simple-keyboard .hg-row:not(:last-child) {
margin-bottom: 5px;
}
.simple-keyboard .hg-row .hg-button:not(:last-child) {
margin-right: 5px;
}
.simple-keyboard .hg-button {
display: inline-block;
-webkit-flex-grow: 1;
flex-grow: 1;
cursor: pointer;
}
.simple-keyboard .hg-button span {
pointer-events: none;
}
/**
* hg-theme-default theme
*/
.simple-keyboard.hg-theme-default {
background-color: rgba(0, 0, 0, 0.1);
padding: 5px;
border-radius: 5px;
}
.simple-keyboard.hg-theme-default .hg-button {
box-shadow: 0px 0px 3px -1px rgba(0, 0, 0, 0.3);
height: 40px;
border-radius: 5px;
box-sizing: border-box;
padding: 5px;
background: white;
border-bottom: 1px solid #b5b5b5;
cursor: pointer;
display: -webkit-flex;
display: flex;
-webkit-align-items: center;
align-items: center;
-webkit-justify-content: center;
justify-content: center;
}
/* When using option "useButtonTag" */
.simple-keyboard button.hg-button {
border-width: 0;
outline: 0;
font-size: inherit;
}
.simple-keyboard.hg-theme-default:not(.hg-touch-events) .hg-button:active {
background: #e4e4e4;
}
.simple-keyboard.hg-theme-default.hg-layout-numeric .hg-button {
width: 33.3%;
height: 60px;
-webkit-align-items: center;
align-items: center;
display: -webkit-flex;
display: flex;
-webkit-justify-content: center;
justify-content: center;
}
.simple-keyboard.hg-theme-default .hg-button.hg-button-numpadadd {
height: 85px;
}
.simple-keyboard.hg-theme-default .hg-button.hg-button-numpadenter {
height: 85px;
}
.simple-keyboard.hg-theme-default .hg-button.hg-button-numpad0 {
width: 105px;
}
.simple-keyboard.hg-theme-default .hg-button.hg-button-com {
max-width: 85px;
}
.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn.hg-button-at {
max-width: 45px;
}
.simple-keyboard.hg-theme-default .hg-button.hg-selectedButton {
background: rgba(5, 25, 70, 0.53);
color: white;
}
.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn[data-skbtn=".com"] {
max-width: 82px;
}
.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn[data-skbtn="@"] {
max-width: 60px;
}
+219
View File
@@ -0,0 +1,219 @@
declare module 'simple-keyboard' {
interface KeyboardLayoutObject {
default: string[];
shift?: string[];
}
interface KeyboardButtonTheme {
class: string;
buttons: string;
}
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[];
/**
* Runs a `console.log` every time a key is pressed. Displays the buttons pressed and the current input.
*/
debug?: boolean;
/**
* Specifies whether clicking the "ENTER" button will input a newline (`\n`) or not.
*/
newLineOnEnter?: boolean;
/**
* Specifies whether clicking the "TAB" button will input a tab character (`\t`) or not.
*/
tabCharOnTab?: boolean;
/**
* Allows you to use a single simple-keyboard instance for several inputs.
*/
inputName?: string;
/**
* `number`: Restrains all of simple-keyboard inputs to a certain length. This should be used in addition to the input elements maxlengthattribute.
*
* `{ [inputName: string]: number }`: Restrains simple-keyboards individual inputs to a certain length. This should be used in addition to the input elements maxlengthattribute.
*/
maxLength?:
| number
| {
[inputName: string]: number;
};
/**
* 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;
/**
* Calling preventDefault for the mousedown events keeps the focus on the input.
*/
preventMouseDownDefault?: 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;
/**
* 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;
/**
* Executes the callback function on key press. Returns button layout name (i.e.: "{shift}").
*/
onKeyPress?: (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;
}
class Keyboard {
constructor(selector: string, options: KeyboardOptions);
constructor(options: KeyboardOptions);
options: KeyboardOptions;
/**
* Adds/Modifies an entry to the `buttonTheme`. Basically a way to add a class to a button.
* @param {string} buttons List of buttons to select (separated by a space).
* @param {string} className Classes to give to the selected buttons (separated by space).
*/
addButtonTheme(buttons: string, className: string): void;
/**
* Removes/Amends an entry to the `buttonTheme`. Basically a way to remove a class previously added to a button through buttonTheme or addButtonTheme.
* @param {string} buttons List of buttons to select (separated by a space).
* @param {string} className Classes to give to the selected buttons (separated by space).
*/
removeButtonTheme(buttons: string, className: string): void;
/**
* Clear the keyboard's input.
*
* @param {string} [inputName] optional - the internal input to select
*/
clearInput(inputName?: string): void;
/**
* Get the keyboards input (You can also get it from the onChange prop).
* @param {string} [inputName] optional - the internal input to select
*/
getInput(inputName?: string): string;
/**
* Set the keyboards input.
* @param {string} input the input value
* @param {string} inputName optional - the internal input to select
*/
setInput(input: string, inputName?: string): void;
/**
* 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[];
}
export default Keyboard;
}
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+9 -6
View File
@@ -27,24 +27,27 @@ var dotenvFiles = [
// Load environment variables from .env* files. Suppress warnings using silent // Load environment variables from .env* files. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables // if this file is missing. dotenv will never modify any environment variables
// that have already been set. // that have already been set. Variable expansion is supported in .env files.
// https://github.com/motdotla/dotenv // https://github.com/motdotla/dotenv
// https://github.com/motdotla/dotenv-expand
dotenvFiles.forEach(dotenvFile => { dotenvFiles.forEach(dotenvFile => {
if (fs.existsSync(dotenvFile)) { if (fs.existsSync(dotenvFile)) {
require('dotenv').config({ require('dotenv-expand')(
path: dotenvFile, require('dotenv').config({
}); path: dotenvFile,
})
);
} }
}); });
// We support resolving modules according to `NODE_PATH`. // We support resolving modules according to `NODE_PATH`.
// This lets you use absolute paths in imports inside large monorepos: // This lets you use absolute paths in imports inside large monorepos:
// https://github.com/facebookincubator/create-react-app/issues/253. // https://github.com/facebook/create-react-app/issues/253.
// It works similar to `NODE_PATH` in Node itself: // It works similar to `NODE_PATH` in Node itself:
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders // 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. // 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. // Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
// https://github.com/facebookincubator/create-react-app/issues/1023#issuecomment-265344421 // https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
// We also resolve them to make sure all tools using them work consistently. // We also resolve them to make sure all tools using them work consistently.
const appDirectory = fs.realpathSync(process.cwd()); const appDirectory = fs.realpathSync(process.cwd());
process.env.NODE_PATH = (process.env.NODE_PATH || '') process.env.NODE_PATH = (process.env.NODE_PATH || '')
+1 -1
View File
@@ -1,7 +1,7 @@
'use strict'; 'use strict';
// This is a custom Jest transformer turning style imports into empty objects. // This is a custom Jest transformer turning style imports into empty objects.
// http://facebook.github.io/jest/docs/tutorial-webpack.html // http://facebook.github.io/jest/docs/en/webpack.html
module.exports = { module.exports = {
process() { process() {
+20 -2
View File
@@ -3,10 +3,28 @@
const path = require('path'); const path = require('path');
// This is a custom Jest transformer turning file imports into filenames. // This is a custom Jest transformer turning file imports into filenames.
// http://facebook.github.io/jest/docs/tutorial-webpack.html // http://facebook.github.io/jest/docs/en/webpack.html
module.exports = { module.exports = {
process(src, filename) { process(src, filename) {
return `module.exports = ${JSON.stringify(path.basename(filename))};`; const assetFilename = JSON.stringify(path.basename(filename));
if (filename.match(/\.svg$/)) {
return `module.exports = {
__esModule: true,
default: ${assetFilename},
ReactComponent: (props) => ({
$$typeof: Symbol.for('react.element'),
type: 'svg',
ref: null,
key: null,
props: Object.assign({}, props, {
children: ${assetFilename}
})
}),
};`;
}
return `module.exports = ${assetFilename};`;
}, },
}; };
+48 -18
View File
@@ -5,20 +5,20 @@ const fs = require('fs');
const url = require('url'); const url = require('url');
// Make sure any symlinks in the project folder are resolved: // Make sure any symlinks in the project folder are resolved:
// https://github.com/facebookincubator/create-react-app/issues/637 // https://github.com/facebook/create-react-app/issues/637
const appDirectory = fs.realpathSync(process.cwd()); const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath); const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
const envPublicUrl = process.env.PUBLIC_URL; const envPublicUrl = process.env.PUBLIC_URL;
function ensureSlash(path, needsSlash) { function ensureSlash(inputPath, needsSlash) {
const hasSlash = path.endsWith('/'); const hasSlash = inputPath.endsWith('/');
if (hasSlash && !needsSlash) { if (hasSlash && !needsSlash) {
return path.substr(path, path.length - 1); return inputPath.substr(0, inputPath.length - 1);
} else if (!hasSlash && needsSlash) { } else if (!hasSlash && needsSlash) {
return `${path}/`; return `${inputPath}/`;
} else { } else {
return path; return inputPath;
} }
} }
@@ -38,27 +38,57 @@ function getServedPath(appPackageJson) {
return ensureSlash(servedUrl, true); return ensureSlash(servedUrl, true);
} }
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/ // config after eject: we're in ./config/
module.exports = { module.exports = {
dotenv: resolveApp('.env'), dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
appBuild: resolveApp('build'), appBuild: resolveApp('build'),
appDemoBuild: resolveApp('demo'), appDemo: resolveApp('demo'),
appPublic: resolveApp('public'), appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'), appHtml: resolveApp('public/index.html'),
appIndexJs: resolveApp('src/demo/index.js'), // CRL: Updated for demo purposes appIndexJs: resolveModule(resolveApp, 'src/demo/index'),
appLibIndexJs: resolveModule(resolveApp, 'src/lib/index'),
appDemoIndexJs: resolveModule(resolveApp, 'src/demo/index'),
appPackageJson: resolveApp('package.json'), appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'), appSrc: resolveApp('src'),
appSrcLib: resolveApp('src/lib'),
appSrcLibTypes: resolveApp('src/lib/@types'),
appSrcDemo: resolveApp('src/demo'),
appTsConfig: resolveApp('tsconfig.json'),
yarnLockFile: resolveApp('yarn.lock'), yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveApp('src/setupTests.js'), testsSetup: resolveModule(resolveApp, 'src/setupTests'),
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'), appNodeModules: resolveApp('node_modules'),
publicUrl: getPublicUrl(resolveApp('package.json')), publicUrl: getPublicUrl(resolveApp('package.json')),
servedPath: getServedPath(resolveApp('package.json')), //servedPath: getServedPath(resolveApp('package.json')),
servedPath: ''
// CRL: New paths for demo build
appDemoIndexJs: resolveApp('src/demo/index.js'),
appDemoSrc: resolveApp('src/demo'),
// CRL: New paths for library
appLibIndexJs: resolveApp('src/lib/index.js'),
appLibSrc: resolveApp('src/lib'),
}; };
module.exports.moduleFileExtensions = moduleFileExtensions;
-22
View File
@@ -1,22 +0,0 @@
'use strict';
if (typeof Promise === 'undefined') {
// Rejection tracking prevents a common issue where React gets into an
// inconsistent state due to an error, but it gets swallowed by a Promise,
// and the user has no idea what causes React's erratic future behavior.
require('promise/lib/rejection-tracking').enable();
window.Promise = require('promise/lib/es6-extensions.js');
}
// fetch() polyfill for making API calls.
require('whatwg-fetch');
// Object.assign() is commonly used with React.
// It will use the native implementation if it's present and isn't buggy.
Object.assign = require('object-assign');
// In tests, polyfill requestAnimationFrame since jsdom doesn't provide it yet.
// We don't polyfill it in the browser--this is user's responsibility.
if (process.env.NODE_ENV === 'test') {
require('raf').polyfill(global);
}
+409 -144
View File
@@ -1,16 +1,27 @@
'use strict';
const fs = require('fs');
const autoprefixer = require('autoprefixer');
const path = require('path'); const path = require('path');
const webpack = require('webpack'); const webpack = require('webpack');
const resolve = require('resolve');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-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 InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths'); const paths = require('./paths');
const getClientEnvironment = require('./env'); const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
// Webpack uses `publicPath` to determine where the app is being served from. // 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. // It requires a trailing slash, or the file assets will get an incorrect path.
@@ -20,10 +31,13 @@ const publicPath = paths.servedPath;
const shouldUseRelativeAssetPaths = publicPath === './'; const shouldUseRelativeAssetPaths = publicPath === './';
// Source maps are resource heavy and can cause out of memory issue for large source files. // Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; 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';
// `publicUrl` is just like `publicPath`, but we will provide it to our app // `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. // 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. // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
const publicUrl = '.'; const publicUrl = publicPath.slice(0, -1);
// Get environment variables to inject into our app. // Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl); const env = getClientEnvironment(publicUrl);
@@ -33,88 +47,228 @@ if (env.stringified['process.env'].NODE_ENV !== '"production"') {
throw new Error('Production builds must have NODE_ENV=production.'); throw new Error('Production builds must have NODE_ENV=production.');
} }
// Note: defined here because it will be used more than once. // Check if TypeScript is setup
const cssFilename = 'css/index.css'; const useTypeScript = fs.existsSync(paths.appTsConfig);
// ExtractTextPlugin expects the build output to be flat. // style files regexes
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27) const cssRegex = /\.css$/;
// However, our output is structured with css, js and media folders. const cssModuleRegex = /\.module\.css$/;
// To have this structure working with relative paths, we have to use custom options. const sassRegex = /\.(scss|sass)$/;
const extractTextPluginOptions = shouldUseRelativeAssetPaths const sassModuleRegex = /\.module\.(scss|sass)$/;
? // Making sure that the publicPath goes back to to build folder.
{ publicPath: Array(cssFilename.split('/').length).join('../') } // common function to get style loaders
: {}; const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
{
loader: MiniCssExtractPlugin.loader,
options: Object.assign(
{},
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined
),
},
{
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,
}),
],
sourceMap: shouldUseSourceMap,
},
},
];
if (preProcessor) {
loaders.push({
loader: require.resolve(preProcessor),
options: {
sourceMap: shouldUseSourceMap,
},
});
}
return loaders;
};
// This is the production configuration. // This is the production configuration.
// It compiles slowly and is focused on producing a fast and minimal bundle. // It compiles slowly and is focused on producing a fast and minimal bundle.
// The development configuration is different and lives in a separate file. // The development configuration is different and lives in a separate file.
module.exports = { module.exports = {
mode: 'production',
// Don't attempt to continue if there are any errors. // Don't attempt to continue if there are any errors.
bail: true, bail: true,
// We generate sourcemaps in production. This is slow but gives good results. // We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment. // You can exclude the *.map files from the build during deployment.
devtool: shouldUseSourceMap ? 'source-map' : false, devtool: shouldUseSourceMap ? 'source-map' : false,
// In production, we only want to load the app code. // In production, we only want to load the app code.
entry: [paths.appDemoIndexJs], // CRL: library index file instead of app index entry: [paths.appDemoIndexJs],
output: { output: {
// CRL: Updated whole block with library specific info // The build folder.
path: paths.appDemoBuild, path: paths.appDemo,
// Generated JS file names (with nested folders).
// There will be one main bundle, and one file per asynchronous chunk.
// We don't currently advertise code splitting but Webpack supports it.
filename: 'index.js', filename: 'index.js',
libraryTarget: 'umd', chunkFilename: 'index.[chunkhash:8].chunk.js',
library: 'SimpleKeyboard' // We inferred the "public path" (such as / or /my-project) from homepage.
publicPath: publicPath,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: info =>
path
.relative(paths.appSrcDemo, info.absoluteResourcePath)
.replace(/\\/g, '/'),
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
parse: {
// we want terser to parse ecma 8 code. However, we don't want it
// to apply any minfication 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 futher investigation:
// https://github.com/terser-js/terser/issues/120
inline: 2,
},
mangle: {
safari10: 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,
},
},
// Use multi-process parallel running to improve the build speed
// Default number of concurrent runs: os.cpus().length - 1
parallel: true,
// Enable file caching
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,
},
}),
],
// 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: {
/*chunks: 'all',
name: false,*/
cacheGroups: {
default: false,
}
},
// Keep the runtime chunk seperated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
runtimeChunk: false,
}, },
resolve: { resolve: {
// This allows you to set a fallback for where Webpack should look for modules. // 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" // We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism. // if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebookincubator/create-react-app/issues/253 // https://github.com/facebook/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat( modules: ['node_modules'].concat(
// It is guaranteed to exist because we tweak it in `env.js` // It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean) process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
), ),
// These are the reasonable defaults supported by the Node ecosystem. // These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support // We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see: // some tools, although we do not recommend using it, see:
// https://github.com/facebookincubator/create-react-app/issues/290 // https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support // `web` extension prefixes have been added for better support
// for React Native Web. // for React Native Web.
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'], extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: { alias: {
// Support React Native Web // Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web' 'react-native': 'react-native-web',
}, },
plugins: [ 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/). // 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. // 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, // 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. // 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. // Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]) 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: { module: {
strictExportPresence: true, strictExportPresence: true,
rules: [ rules: [
// TODO: Disable require.ensure as it's not a standard language feature. // Disable require.ensure as it's not a standard language feature.
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176. { parser: { requireEnsure: false } },
// { parser: { requireEnsure: false } },
// First, run the linter. // First, run the linter.
// It's important to do this before Babel processes the JS. // It's important to do this before Babel processes the JS.
{ {
test: /\.(js|jsx|mjs)$/, test: /\.(js|mjs|jsx)$/,
enforce: 'pre', enforce: 'pre',
use: [ use: [
{ {
options: { options: {
formatter: eslintFormatter, formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint') eslintPath: require.resolve('eslint'),
}, },
loader: require.resolve('eslint-loader') loader: require.resolve('eslint-loader'),
} },
], ],
include: paths.appDemoSrc // CRL: updated with library src folder include: paths.appSrcDemo,
}, },
{ {
// "oneOf" will traverse all following loaders until one will // "oneOf" will traverse all following loaders until one will
@@ -128,76 +282,132 @@ module.exports = {
loader: require.resolve('url-loader'), loader: require.resolve('url-loader'),
options: { options: {
limit: 10000, limit: 10000,
name: 'media/[name].[ext]' name: 'static/media/[name].[hash:8].[ext]',
} },
}, },
// Process JS with Babel. // Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript and some ESnext features.
{ {
test: /\.(js|jsx|mjs)$/, test: /\.(js|mjs|jsx|ts|tsx)$/,
include: [paths.appLibSrc, paths.appDemoSrc], // CRL: updated with library src folder include: paths.appSrcDemo,
loader: require.resolve('babel-loader'), loader: require.resolve('babel-loader'),
options: { options: {
compact: true customize: require.resolve(
} 'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent: '@svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
],
cacheDirectory: true,
// Save disk space when time isn't as important
cacheCompression: true,
compact: true,
},
},
// 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: [
"@babel/preset-env",
"@babel/preset-react"
],
plugins: [
[
"@babel/plugin-proposal-class-properties"
]
],
cacheDirectory: true,
// Save disk space when time isn't as important
cacheCompression: true,
// If an error happens in a package, it's possible to be
// because it was compiled. Thus, we don't want the browser
// debugger to show the original code. Instead, the code
// being evaluated would be much more helpful.
sourceMaps: false,
},
}, },
// The notation here is somewhat confusing.
// "postcss" loader applies autoprefixer to our CSS. // "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies. // "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader normally turns CSS into JS modules injecting <style>, // `MiniCSSExtractPlugin` extracts styles into CSS
// but unlike in development configuration, we do something different. // files. If you use code splitting, async bundles will have their own separate CSS chunk file.
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders // By default we support CSS Modules with the extension .module.css
// (second argument), then grabs the result CSS and puts it into a
// separate file in our build process. This way we actually ship
// a single CSS file in production instead of JS code injecting <style>
// tags. If you use code splitting, however, any async bundles will still
// use the "style" loader inside the async code so CSS from them won't be
// in the main CSS file.
{ {
test: /\.css$/, test: cssRegex,
loader: ExtractTextPlugin.extract( exclude: cssModuleRegex,
Object.assign( loader: getStyleLoaders({
{ importLoaders: 1,
fallback: { sourceMap: shouldUseSourceMap,
loader: require.resolve('style-loader'), }),
options: { // Don't consider CSS imports dead code even if the
hmr: false // 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
use: [ sideEffects: true,
{ },
loader: require.resolve('css-loader'), // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
options: { // using the extension .module.css
importLoaders: 1, {
minimize: true, test: cssModuleRegex,
sourceMap: shouldUseSourceMap loader: getStyleLoaders({
} importLoaders: 1,
}, sourceMap: shouldUseSourceMap,
{ modules: true,
loader: require.resolve('postcss-loader'), getLocalIdent: getCSSModuleLocalIdent,
options: { }),
// Necessary for external CSS imports to work },
// https://github.com/facebookincubator/create-react-app/issues/2677 // Opt-in support for SASS. The logic here is somewhat similar
ident: 'postcss', // as in the CSS routine, except that "sass-loader" runs first
plugins: () => [ // to compile SASS files into CSS.
require('postcss-flexbugs-fixes'), // By default we support SASS Modules with the
autoprefixer({ // extensions .module.scss or .module.sass
browsers: [ {
'>1%', test: sassRegex,
'last 4 versions', exclude: sassModuleRegex,
'Firefox ESR', loader: getStyleLoaders(
'not ie < 9' // React doesn't support IE8 anyway {
], importLoaders: 2,
flexbox: 'no-2009' sourceMap: 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.
extractTextPluginOptions // See https://github.com/webpack/webpack/issues/6571
) sideEffects: true,
) },
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`. // Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
loader: getStyleLoaders(
{
importLoaders: 2,
sourceMap: shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
}, },
// "file" loader makes sure assets end up in the `build` folder. // "file" loader makes sure assets end up in the `build` folder.
// When you `import` an asset, you get its filename. // When you `import` an asset, you get its filename.
@@ -206,68 +416,120 @@ module.exports = {
{ {
loader: require.resolve('file-loader'), loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects // Exclude `js` files to keep "css" loader working as it injects
// it's runtime that would otherwise processed through "file" loader. // it's runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed // Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders. // by webpacks internal loaders.
exclude: [/\.js$/, /\.html$/, /\.json$/], exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: { options: {
name: 'media/[name].[ext]' name: 'static/media/[name].[hash:8].[ext]',
} },
} },
// ** STOP ** Are you adding a new loader? // ** STOP ** Are you adding a new loader?
// Make sure to add the new loader(s) before the "file" loader. // Make sure to add the new loader(s) before the "file" loader.
] ],
} },
] ],
}, },
plugins: [ plugins: [
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),
// Inlines the webpack runtime script. This script is too small to warrant
// a network request.
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="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In production, 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: // Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`. // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV was set to production here. // It is absolutely essential that NODE_ENV was set to production here.
// Otherwise React will be compiled in the very slow development mode. // Otherwise React will be compiled in the very slow development mode.
new InterpolateHtmlPlugin(env.raw),
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml
}),
// Add module names to factory functions so they appear in browser profiler.
new webpack.NamedModulesPlugin(),
new webpack.DefinePlugin(env.stringified), new webpack.DefinePlugin(env.stringified),
// Minify the code. new MiniCssExtractPlugin({
new webpack.optimize.UglifyJsPlugin({ // Options similar to the same options in webpackOptions.output
compress: false, // both options are optional
mangle: { filename: 'index.css',
safari10: true chunkFilename: 'index.[contenthash:8].chunk.css',
},
output: {
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebookincubator/create-react-app/issues/2488
ascii_only: true
},
sourceMap: shouldUseSourceMap
}),
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
new ExtractTextPlugin({
filename: cssFilename
}), }),
// Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
/*new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
}),*/
// Moment.js is an extremely popular library that bundles large locale files // 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 // by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales. // solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js: // You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
new CopyWebpackPlugin([ // Generate a service worker script that will precache, and keep up to date,
{ from: `${paths.appPublic}/favicon.ico`, to: paths.appDemoBuild }, // the HTML & assets that are part of the Webpack build.
{ from: `${paths.appPublic}/manifest.json`, to: paths.appDemoBuild } /*new WorkboxWebpackPlugin.GenerateSW({
]) clientsClaim: true,
], exclude: [/\.map$/, /asset-manifest\.json$/],
// CRL: added externals block for library importWorkboxFrom: 'cdn',
// externals: { navigateFallback: publicUrl + '/index.html',
// 'react': 'react', navigateFallbackBlacklist: [
// 'react-dom': 'react-dom' // Exclude URLs starting with /_, as they're likely an API call
// }, new RegExp('^/_'),
// Exclude URLs containing a dot, as they're likely a resource in
// public/ and not a SPA route
new RegExp('/[^/]+\\.[^/]+$'),
],
}),*/
// TypeScript type checking
fs.existsSync(paths.appTsConfig) &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
async: false,
checkSyntacticErrors: true,
tsconfig: paths.appTsConfig,
compilerOptions: {
module: 'esnext',
moduleResolution: 'node',
resolveJsonModule: true,
isolatedModules: true,
noEmit: true,
jsx: 'preserve',
},
reportFiles: [
'**',
'!**/*.json',
'!**/__tests__/**',
'!**/?(*.)(spec|test).*',
'!src/setupProxy.js',
'!src/setupTests.*',
],
watch: paths.appSrcDemo,
silent: true,
formatter: typescriptFormatter,
}),
].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser. // 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. // Tell Webpack to provide empty mocks for them so importing them works.
node: { node: {
@@ -275,6 +537,9 @@ module.exports = {
fs: 'empty', fs: 'empty',
net: 'empty', net: 'empty',
tls: 'empty', tls: 'empty',
child_process: 'empty' child_process: 'empty',
} },
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
}; };
+227 -63
View File
@@ -1,16 +1,23 @@
'use strict'; 'use strict';
const autoprefixer = require('autoprefixer'); const fs = require('fs');
const path = require('path'); const path = require('path');
const resolve = require('resolve');
const webpack = require('webpack'); const webpack = require('webpack');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const getClientEnvironment = require('./env'); const getClientEnvironment = require('./env');
const paths = require('./paths'); const paths = require('./paths');
const ManifestPlugin = require('webpack-manifest-plugin');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
// Webpack uses `publicPath` to determine where the app is being served from. // Webpack uses `publicPath` to determine where the app is being served from.
// In development, we always serve from the root. This makes config easier. // In development, we always serve from the root. This makes config easier.
@@ -22,16 +29,60 @@ const publicUrl = '';
// Get environment variables to inject into our app. // Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl); const env = getClientEnvironment(publicUrl);
// 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)$/;
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
require.resolve('style-loader'),
{
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,
}),
],
},
},
];
if (preProcessor) {
loaders.push(require.resolve(preProcessor));
}
return loaders;
};
// This is the development configuration. // This is the development configuration.
// It is focused on developer experience and fast rebuilds. // It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file. // The production configuration is different and lives in a separate file.
module.exports = { module.exports = {
mode: 'development',
// You may want 'eval' instead if you prefer to see the compiled output in DevTools. // You may want 'eval' instead if you prefer to see the compiled output in DevTools.
// See the discussion in https://github.com/facebookincubator/create-react-app/issues/343. // See the discussion in https://github.com/facebook/create-react-app/issues/343
devtool: 'cheap-module-source-map', devtool: 'cheap-module-source-map',
// These are the "entry points" to our application. // These are the "entry points" to our application.
// This means they will be the "root" imports that are included in JS bundle. // This means they will be the "root" imports that are included in JS bundle.
// The first two entry points enable "hot" CSS and auto-refreshes for JS.
entry: [ entry: [
// Include an alternative client for WebpackDevServer. A client's job is to // Include an alternative client for WebpackDevServer. A client's job is to
// connect to WebpackDevServer by a socket and get notified about changes. // connect to WebpackDevServer by a socket and get notified about changes.
@@ -65,29 +116,46 @@ module.exports = {
devtoolModuleFilenameTemplate: info => devtoolModuleFilenameTemplate: info =>
path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'), path.resolve(info.absoluteResourcePath).replace(/\\/g, '/'),
}, },
optimization: {
// 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 seperated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
runtimeChunk: false,
},
resolve: { resolve: {
// This allows you to set a fallback for where Webpack should look for modules. // 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" // We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism. // if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebookincubator/create-react-app/issues/253 // https://github.com/facebook/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat( modules: ['node_modules'].concat(
// It is guaranteed to exist because we tweak it in `env.js` // It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean) process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
), ),
// These are the reasonable defaults supported by the Node ecosystem. // These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support // We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see: // some tools, although we do not recommend using it, see:
// https://github.com/facebookincubator/create-react-app/issues/290 // https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support // `web` extension prefixes have been added for better support
// for React Native Web. // for React Native Web.
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'], extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: { alias: {
// Support React Native Web // Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web', 'react-native': 'react-native-web',
}, },
plugins: [ 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/). // 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. // 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, // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
@@ -96,24 +164,30 @@ module.exports = {
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), 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: { module: {
strictExportPresence: true, strictExportPresence: true,
rules: [ rules: [
// TODO: Disable require.ensure as it's not a standard language feature. // Disable require.ensure as it's not a standard language feature.
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176. { parser: { requireEnsure: false } },
// { parser: { requireEnsure: false } },
// First, run the linter. // First, run the linter.
// It's important to do this before Babel processes the JS. // It's important to do this before Babel processes the JS.
{ {
test: /\.(js|jsx|mjs)$/, test: /\.(js|mjs|jsx)$/,
enforce: 'pre', enforce: 'pre',
use: [ use: [
{ {
options: { options: {
formatter: eslintFormatter, formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'), eslintPath: require.resolve('eslint'),
}, },
loader: require.resolve('eslint-loader'), loader: require.resolve('eslint-loader'),
}, },
@@ -136,16 +210,62 @@ module.exports = {
name: 'static/media/[name].[hash:8].[ext]', name: 'static/media/[name].[hash:8].[ext]',
}, },
}, },
// Process JS with Babel. // Process application JS with Babel.
// The preset includes JSX, Flow, and some ESnext features.
{ {
test: /\.(js|jsx|mjs)$/, test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrc, include: paths.appSrc,
loader: require.resolve('babel-loader'), loader: require.resolve('babel-loader'),
options: { options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent: '@svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
],
// This is a feature of `babel-loader` for webpack (not Babel itself). // This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/ // It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds. // directory for faster rebuilds.
cacheDirectory: true, cacheDirectory: true,
// Don't waste time on Gzipping the cache
cacheCompression: false,
},
},
// 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 },
],
],
cacheDirectory: true,
// Don't waste time on Gzipping the cache
cacheCompression: false,
// If an error happens in a package, it's possible to be
// because it was compiled. Thus, we don't want the browser
// debugger to show the original code. Instead, the code
// being evaluated would be much more helpful.
sourceMaps: false,
}, },
}, },
// "postcss" loader applies autoprefixer to our CSS. // "postcss" loader applies autoprefixer to our CSS.
@@ -153,37 +273,46 @@ module.exports = {
// "style" loader turns CSS into JS modules that inject <style> tags. // "style" loader turns CSS into JS modules that inject <style> tags.
// In production, we use a plugin to extract that CSS to a file, but // In production, we use a plugin to extract that CSS to a file, but
// in development "style" loader enables hot editing of CSS. // in development "style" loader enables hot editing of CSS.
// By default we support CSS Modules with the extension .module.css
{ {
test: /\.css$/, test: cssRegex,
use: [ exclude: cssModuleRegex,
require.resolve('style-loader'), use: getStyleLoaders({
importLoaders: 1,
}),
},
// Adds support for CSS Modules (https://github.com/css-modules/css-modules)
// using the extension .module.css
{
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
// Opt-in support for SASS (using .scss or .sass extensions).
// Chains the sass-loader with the css-loader and the style-loader
// to immediately apply all styles to the DOM.
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
use: getStyleLoaders({ importLoaders: 2 }, 'sass-loader'),
},
// Adds support for CSS Modules, but using SASS
// using the extension .module.scss or .module.sass
{
test: sassModuleRegex,
use: getStyleLoaders(
{ {
loader: require.resolve('css-loader'), importLoaders: 2,
options: { modules: true,
importLoaders: 1, getLocalIdent: getCSSModuleLocalIdent,
},
}, },
{ 'sass-loader'
loader: require.resolve('postcss-loader'), ),
options: {
// Necessary for external CSS imports to work
// https://github.com/facebookincubator/create-react-app/issues/2677
ident: 'postcss',
plugins: () => [
require('postcss-flexbugs-fixes'),
autoprefixer({
browsers: [
'>1%',
'last 4 versions',
'Firefox ESR',
'not ie < 9', // React doesn't support IE8 anyway
],
flexbox: 'no-2009',
}),
],
},
},
],
}, },
// "file" loader makes sure those assets get served by WebpackDevServer. // "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename. // When you `import` an asset, you get its (virtual) filename.
@@ -192,10 +321,10 @@ module.exports = {
// that fall through the other loaders. // that fall through the other loaders.
{ {
// Exclude `js` files to keep "css" loader working as it injects // Exclude `js` files to keep "css" loader working as it injects
// it's runtime that would otherwise processed through "file" loader. // its runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed // Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders. // by webpacks internal loaders.
exclude: [/\.js$/, /\.html$/, /\.json$/], exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
loader: require.resolve('file-loader'), loader: require.resolve('file-loader'),
options: { options: {
name: 'static/media/[name].[hash:8].[ext]', name: 'static/media/[name].[hash:8].[ext]',
@@ -208,18 +337,19 @@ module.exports = {
], ],
}, },
plugins: [ plugins: [
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In development, this will be an empty string.
new InterpolateHtmlPlugin(env.raw),
// Generates an `index.html` file with the <script> injected. // Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({ new HtmlWebpackPlugin({
inject: true, inject: true,
template: paths.appHtml, template: paths.appHtml,
}), }),
// Add module names to factory functions so they appear in browser profiler. // Makes some environment variables available in index.html.
new webpack.NamedModulesPlugin(), // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
// <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In development, this will be an empty string.
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: // Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`. // if (process.env.NODE_ENV === 'development') { ... }. See `./env.js`.
new webpack.DefinePlugin(env.stringified), new webpack.DefinePlugin(env.stringified),
@@ -227,12 +357,12 @@ module.exports = {
new webpack.HotModuleReplacementPlugin(), new webpack.HotModuleReplacementPlugin(),
// Watcher doesn't work well if you mistype casing in a path so we use // 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. // a plugin that prints an error when you attempt to do this.
// See https://github.com/facebookincubator/create-react-app/issues/240 // See https://github.com/facebook/create-react-app/issues/240
new CaseSensitivePathsPlugin(), new CaseSensitivePathsPlugin(),
// If you require a missing module and then `npm install` it, you still have // 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 // to restart the development server for Webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart. // makes the discovery automatic so you don't have to restart.
// See https://github.com/facebookincubator/create-react-app/issues/186 // See https://github.com/facebook/create-react-app/issues/186
new WatchMissingNodeModulesPlugin(paths.appNodeModules), new WatchMissingNodeModulesPlugin(paths.appNodeModules),
// Moment.js is an extremely popular library that bundles large locale files // 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 // by default due to how Webpack interprets its code. This is a practical
@@ -240,7 +370,44 @@ module.exports = {
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js: // You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
], // Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
}),
// TypeScript type checking
useTypeScript &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
async: false,
checkSyntacticErrors: true,
tsconfig: paths.appTsConfig,
compilerOptions: {
module: 'esnext',
moduleResolution: 'node',
resolveJsonModule: true,
isolatedModules: true,
noEmit: true,
jsx: 'preserve',
},
reportFiles: [
'**',
'!**/*.json',
'!**/__tests__/**',
'!**/?(*.)(spec|test).*',
'!src/setupProxy.js',
'!src/setupTests.*',
],
watch: paths.appSrc,
silent: true,
formatter: typescriptFormatter,
}),
].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser. // 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. // Tell Webpack to provide empty mocks for them so importing them works.
node: { node: {
@@ -250,10 +417,7 @@ module.exports = {
tls: 'empty', tls: 'empty',
child_process: 'empty', child_process: 'empty',
}, },
// Turn off performance hints during development because we don't do any // Turn off performance processing because we utilize
// splitting or minification in interest of speed. These warnings become // our own hints via the FileSizeReporter
// cumbersome. performance: false,
performance: {
hints: false,
},
}; };
+403 -125
View File
@@ -1,14 +1,27 @@
'use strict'; 'use strict';
const autoprefixer = require('autoprefixer'); const fs = require('fs');
const path = require('path'); const path = require('path');
const webpack = require('webpack'); const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); const resolve = require('resolve');
const eslintFormatter = require('react-dev-utils/eslintFormatter'); const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-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 InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths'); const paths = require('./paths');
const getClientEnvironment = require('./env'); const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const getPackageJson = require('./getPackageJson'); const getPackageJson = require('./getPackageJson');
var PrettierPlugin = require("prettier-webpack-plugin");
// Webpack uses `publicPath` to determine where the app is being served from. // 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. // It requires a trailing slash, or the file assets will get an incorrect path.
@@ -18,6 +31,9 @@ const publicPath = paths.servedPath;
const shouldUseRelativeAssetPaths = publicPath === './'; const shouldUseRelativeAssetPaths = publicPath === './';
// Source maps are resource heavy and can cause out of memory issue for large source files. // Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; 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';
// `publicUrl` is just like `publicPath`, but we will provide it to our app // `publicUrl` is just like `publicPath`, but we will provide it to our app
// as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript. // 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. // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
@@ -31,17 +47,14 @@ if (env.stringified['process.env'].NODE_ENV !== '"production"') {
throw new Error('Production builds must have NODE_ENV=production.'); throw new Error('Production builds must have NODE_ENV=production.');
} }
// Note: defined here because it will be used more than once. // Check if TypeScript is setup
const cssFilename = 'css/index.css'; const useTypeScript = fs.existsSync(paths.appTsConfig);
// ExtractTextPlugin expects the build output to be flat. // style files regexes
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27) const cssRegex = /\.css$/;
// However, our output is structured with css, js and media folders. const cssModuleRegex = /\.module\.css$/;
// To have this structure working with relative paths, we have to use custom options. const sassRegex = /\.(scss|sass)$/;
const extractTextPluginOptions = shouldUseRelativeAssetPaths const sassModuleRegex = /\.module\.(scss|sass)$/;
? // Making sure that the publicPath goes back to to build folder.
{ publicPath: Array(cssFilename.split('/').length).join('../') }
: {};
const { const {
version, version,
@@ -61,78 +74,228 @@ const banner = `
LICENSE file in the root directory of this source tree. LICENSE file in the root directory of this source tree.
`; `;
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
{
loader: MiniCssExtractPlugin.loader,
options: Object.assign(
{},
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined
),
},
{
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,
}),
],
sourceMap: shouldUseSourceMap,
},
},
];
if (preProcessor) {
loaders.push({
loader: require.resolve(preProcessor),
options: {
sourceMap: shouldUseSourceMap,
},
});
}
return loaders;
};
// This is the production configuration. // This is the production configuration.
// It compiles slowly and is focused on producing a fast and minimal bundle. // It compiles slowly and is focused on producing a fast and minimal bundle.
// The development configuration is different and lives in a separate file. // The development configuration is different and lives in a separate file.
module.exports = { module.exports = {
mode: 'production',
// Don't attempt to continue if there are any errors. // Don't attempt to continue if there are any errors.
bail: true, bail: true,
// We generate sourcemaps in production. This is slow but gives good results. // We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment. // You can exclude the *.map files from the build during deployment.
devtool: shouldUseSourceMap ? 'source-map' : false, devtool: shouldUseSourceMap ? 'source-map' : false,
// In production, we only want to load the app code. // In production, we only want to load the app code.
entry: [paths.appLibIndexJs], // CRL: library index file instead of app index entry: [paths.appLibIndexJs],
output: { output: {
// CRL: Updated whole block with library specific info // The build folder.
path: paths.appBuild, path: paths.appBuild,
// Generated JS file names (with nested folders).
// There will be one main bundle, and one file per asynchronous chunk.
// We don't currently advertise code splitting but Webpack supports it.
filename: 'index.js', filename: 'index.js',
chunkFilename: 'index.[chunkhash:8].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
publicPath: publicPath,
library: "SimpleKeyboard",
libraryTarget: 'umd', libraryTarget: 'umd',
library: 'SimpleKeyboard' umdNamedDefine: true,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: info =>
path
.relative(paths.appSrcLib, info.absoluteResourcePath)
.replace(/\\/g, '/'),
},
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
parse: {
// we want terser to parse ecma 8 code. However, we don't want it
// to apply any minfication 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 futher 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,
},
},
// Use multi-process parallel running to improve the build speed
// Default number of concurrent runs: os.cpus().length - 1
parallel: true,
// Enable file caching
cache: true,
sourceMap: shouldUseSourceMap,
}),
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,
},
}),
],
// 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: {
//chunks: 'all',
cacheGroups: {
default: false,
}
},
// Keep the runtime chunk seperated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
runtimeChunk: false,
}, },
resolve: { resolve: {
// This allows you to set a fallback for where Webpack should look for modules. // 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" // We placed these paths second because we want `node_modules` to "win"
// if there are any conflicts. This matches Node resolution mechanism. // if there are any conflicts. This matches Node resolution mechanism.
// https://github.com/facebookincubator/create-react-app/issues/253 // https://github.com/facebook/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat( modules: ['node_modules'].concat(
// It is guaranteed to exist because we tweak it in `env.js` // It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean) process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
), ),
// These are the reasonable defaults supported by the Node ecosystem. // These are the reasonable defaults supported by the Node ecosystem.
// We also include JSX as a common component filename extension to support // We also include JSX as a common component filename extension to support
// some tools, although we do not recommend using it, see: // some tools, although we do not recommend using it, see:
// https://github.com/facebookincubator/create-react-app/issues/290 // https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support // `web` extension prefixes have been added for better support
// for React Native Web. // for React Native Web.
extensions: ['.web.js', '.mjs', '.js', '.json', '.web.jsx', '.jsx'], extensions: paths.moduleFileExtensions
.map(ext => `.${ext}`)
.filter(ext => useTypeScript || !ext.includes('ts')),
alias: { alias: {
// Support React Native Web // Support React Native Web
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
'react-native': 'react-native-web', 'react-native': 'react-native-web',
}, },
plugins: [ 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/). // 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. // 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, // 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. // 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. // Make sure your source files are compiled, as they will not be processed in any way.
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]), new ModuleScopePlugin(paths.appSrcLib, [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: { module: {
strictExportPresence: true, strictExportPresence: true,
rules: [ rules: [
// TODO: Disable require.ensure as it's not a standard language feature. // Disable require.ensure as it's not a standard language feature.
// We are waiting for https://github.com/facebookincubator/create-react-app/issues/2176. { parser: { requireEnsure: false } },
// { parser: { requireEnsure: false } },
// First, run the linter. // First, run the linter.
// It's important to do this before Babel processes the JS. // It's important to do this before Babel processes the JS.
{ {
test: /\.(js|jsx|mjs)$/, test: /\.(js|mjs|jsx)$/,
enforce: 'pre', enforce: 'pre',
use: [ use: [
{ {
options: { options: {
formatter: eslintFormatter, formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'), eslintPath: require.resolve('eslint'),
}, },
loader: require.resolve('eslint-loader'), loader: require.resolve('eslint-loader'),
}, },
], ],
include: paths.appLibSrc, // CRL: updated with library src folder include: paths.appSrcLib,
}, },
{ {
// "oneOf" will traverse all following loaders until one will // "oneOf" will traverse all following loaders until one will
@@ -146,77 +309,129 @@ module.exports = {
loader: require.resolve('url-loader'), loader: require.resolve('url-loader'),
options: { options: {
limit: 10000, limit: 10000,
name: 'media/[name].[ext]', name: 'static/media/[name].[hash:8].[ext]',
}, },
}, },
// Process JS with Babel. // Process application JS with Babel.
// The preset includes JSX, Flow, TypeScript and some ESnext features.
{ {
test: /\.(js|jsx|mjs)$/, test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appLibSrc, // CRL: updated with library src folder include: paths.appSrcLib,
loader: require.resolve('babel-loader'), loader: require.resolve('babel-loader'),
options: { options: {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
plugins: [
[
require.resolve('babel-plugin-named-asset-import'),
{
loaderMap: {
svg: {
ReactComponent: '@svgr/webpack?-prettier,-svgo![path]',
},
},
},
],
],
cacheDirectory: true,
// Save disk space when time isn't as important
cacheCompression: true,
compact: true, compact: true,
}, },
}, },
// The notation here is somewhat confusing. // 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 },
],
],
cacheDirectory: true,
// Save disk space when time isn't as important
cacheCompression: true,
// If an error happens in a package, it's possible to be
// because it was compiled. Thus, we don't want the browser
// debugger to show the original code. Instead, the code
// being evaluated would be much more helpful.
sourceMaps: false,
},
},
// "postcss" loader applies autoprefixer to our CSS. // "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies. // "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader normally turns CSS into JS modules injecting <style>, // `MiniCSSExtractPlugin` extracts styles into CSS
// but unlike in development configuration, we do something different. // files. If you use code splitting, async bundles will have their own separate CSS chunk file.
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders // By default we support CSS Modules with the extension .module.css
// (second argument), then grabs the result CSS and puts it into a
// separate file in our build process. This way we actually ship
// a single CSS file in production instead of JS code injecting <style>
// tags. If you use code splitting, however, any async bundles will still
// use the "style" loader inside the async code so CSS from them won't be
// in the main CSS file.
{ {
test: /\.css$/, test: cssRegex,
loader: ExtractTextPlugin.extract( exclude: cssModuleRegex,
Object.assign( loader: getStyleLoaders({
{ importLoaders: 1,
fallback: { sourceMap: shouldUseSourceMap,
loader: require.resolve('style-loader'), }),
options: { // Don't consider CSS imports dead code even if the
hmr: false, // 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
use: [ sideEffects: true,
{ },
loader: require.resolve('css-loader'), // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
options: { // using the extension .module.css
importLoaders: 1, {
minimize: true, test: cssModuleRegex,
sourceMap: shouldUseSourceMap, loader: getStyleLoaders({
}, importLoaders: 1,
}, sourceMap: shouldUseSourceMap,
{ modules: true,
loader: require.resolve('postcss-loader'), getLocalIdent: getCSSModuleLocalIdent,
options: { }),
// Necessary for external CSS imports to work },
// https://github.com/facebookincubator/create-react-app/issues/2677 // Opt-in support for SASS. The logic here is somewhat similar
ident: 'postcss', // as in the CSS routine, except that "sass-loader" runs first
plugins: () => [ // to compile SASS files into CSS.
require('postcss-flexbugs-fixes'), // By default we support SASS Modules with the
autoprefixer({ // extensions .module.scss or .module.sass
browsers: [ {
'>1%', test: sassRegex,
'last 4 versions', exclude: sassModuleRegex,
'Firefox ESR', loader: getStyleLoaders(
'not ie < 9', // React doesn't support IE8 anyway {
], importLoaders: 2,
flexbox: 'no-2009', sourceMap: 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.
extractTextPluginOptions // 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,
loader: getStyleLoaders(
{
importLoaders: 2,
sourceMap: shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
), ),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
}, },
// "file" loader makes sure assets end up in the `build` folder. // "file" loader makes sure assets end up in the `build` folder.
// When you `import` an asset, you get its filename. // When you `import` an asset, you get its filename.
@@ -225,12 +440,12 @@ module.exports = {
{ {
loader: require.resolve('file-loader'), loader: require.resolve('file-loader'),
// Exclude `js` files to keep "css" loader working as it injects // Exclude `js` files to keep "css" loader working as it injects
// it's runtime that would otherwise processed through "file" loader. // it's runtime that would otherwise be processed through "file" loader.
// Also exclude `html` and `json` extensions so they get processed // Also exclude `html` and `json` extensions so they get processed
// by webpacks internal loaders. // by webpacks internal loaders.
exclude: [/\.js$/, /\.html$/, /\.json$/], exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: { options: {
name: 'media/[name].[ext]', name: 'static/media/[name].[hash:8].[ext]',
}, },
}, },
// ** STOP ** Are you adding a new loader? // ** STOP ** Are you adding a new loader?
@@ -240,52 +455,112 @@ module.exports = {
], ],
}, },
plugins: [ plugins: [
// Generates an `index.html` file with the <script> injected.
/*new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),*/
// Inlines the webpack runtime script. This script is too small to warrant
// a network request.
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="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In production, 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: // Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`. // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV was set to production here. // It is absolutely essential that NODE_ENV was set to production here.
// Otherwise React will be compiled in the very slow development mode. // Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified), new webpack.DefinePlugin(env.stringified),
// Minify the code. new MiniCssExtractPlugin({
new webpack.optimize.UglifyJsPlugin({ // Options similar to the same options in webpackOptions.output
compress: { // both options are optional
warnings: false, filename: 'css/index.css',
// Disabled because of an issue with Uglify breaking seemingly valid code: chunkFilename: 'css/index.[contenthash:8].chunk.css',
// https://github.com/facebookincubator/create-react-app/issues/2376
// Pending further investigation:
// https://github.com/mishoo/UglifyJS2/issues/2011
comparisons: false,
},
mangle: {
safari10: true,
},
output: {
comments: false,
// Turned on because emoji and regex is not minified properly using default
// https://github.com/facebookincubator/create-react-app/issues/2488
ascii_only: true,
},
sourceMap: shouldUseSourceMap,
}),
new webpack.BannerPlugin({
banner: banner,
entryOnly: true
}),
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
new ExtractTextPlugin({
filename: cssFilename,
}), }),
// Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
/*new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
}),*/
// Moment.js is an extremely popular library that bundles large locale files // 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 // by default due to how Webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales. // solution that requires the user to opt into importing specific locales.
// https://github.com/jmblog/how-to-optimize-momentjs-with-webpack // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
// You can remove this if you don't use Moment.js: // You can remove this if you don't use Moment.js:
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
], new CopyWebpackPlugin([
// CRL: added externals block for library {
externals: { from: `${paths.appSrcLibTypes}`,
'react': 'react', to: paths.appBuild
'react-dom': 'react-dom' }
}, ]),
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.
/*new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
exclude: [/\.map$/, /asset-manifest\.json$/],
importWorkboxFrom: 'cdn',
navigateFallback: publicUrl + '/index.html',
navigateFallbackBlacklist: [
// Exclude URLs starting with /_, as they're likely an API call
new RegExp('^/_'),
// Exclude URLs containing a dot, as they're likely a resource in
// public/ and not a SPA route
new RegExp('/[^/]+\\.[^/]+$'),
],
}),*/
// TypeScript type checking
fs.existsSync(paths.appTsConfig) &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
async: false,
checkSyntacticErrors: true,
tsconfig: paths.appTsConfig,
compilerOptions: {
module: 'esnext',
moduleResolution: 'node',
resolveJsonModule: true,
isolatedModules: true,
noEmit: true,
jsx: 'preserve',
},
reportFiles: [
'**',
'!**/*.json',
'!**/__tests__/**',
'!**/?(*.)(spec|test).*',
'!src/setupProxy.js',
'!src/setupTests.*',
],
watch: paths.appSrcLib,
silent: true,
formatter: typescriptFormatter,
}),
].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser. // 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. // Tell Webpack to provide empty mocks for them so importing them works.
node: { node: {
@@ -295,4 +570,7 @@ module.exports = {
tls: 'empty', tls: 'empty',
child_process: 'empty', child_process: 'empty',
}, },
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
}; };
+504
View File
@@ -0,0 +1,504 @@
'use strict';
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 InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const paths = require('./paths');
const getClientEnvironment = require('./env');
const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin-alt');
const typescriptFormatter = require('react-dev-utils/typescriptFormatter');
const getPackageJson = require('./getPackageJson');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
// 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.
const publicPath = paths.servedPath;
// Some apps do not use client-side routing with pushState.
// For these, "homepage" can be set to "." to enable relative asset paths.
const shouldUseRelativeAssetPaths = publicPath === './';
// 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';
// `publicUrl` is just like `publicPath`, but we will provide it 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.
const publicUrl = publicPath.slice(0, -1);
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);
// 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} (Non-minified build)
${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.
`;
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
{
loader: MiniCssExtractPlugin.loader,
options: Object.assign(
{},
shouldUseRelativeAssetPaths ? { publicPath: '../../' } : undefined
),
},
{
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,
}),
],
sourceMap: shouldUseSourceMap,
},
},
];
if (preProcessor) {
loaders.push({
loader: require.resolve(preProcessor),
options: {
sourceMap: shouldUseSourceMap,
},
});
}
return loaders;
};
// This is the production configuration.
// It compiles slowly and is focused on producing a fast and minimal bundle.
// The development configuration is different and lives in a separate file.
module.exports = {
mode: 'production',
// Don't attempt to continue if there are any errors.
bail: true,
// We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment.
devtool: false,
// In production, we only want to load the app code.
entry: [paths.appLibIndexJs],
output: {
// The build folder.
path: paths.appBuild,
// Generated JS file names (with nested folders).
// There will be one main bundle, and one file per asynchronous chunk.
// We don't currently advertise code splitting but Webpack supports it.
filename: 'simple-keyboard.js',
chunkFilename: 'index.[chunkhash:8].chunk.js',
// We inferred the "public path" (such as / or /my-project) from homepage.
publicPath: publicPath,
library: "SimpleKeyboard",
libraryTarget: 'umd',
umdNamedDefine: true,
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: info =>
path
.relative(paths.appSrcLib, info.absoluteResourcePath)
.replace(/\\/g, '/'),
},
optimization: {
minimizer: [
// we specify a custom UglifyJsPlugin here to get source maps in production
new UglifyJsPlugin({
cache: false,
parallel: false,
uglifyOptions: {
output: {
comments: true,
beautify: true,
braces: true,
indent_level: 2
},
mangle: false,
compress: false
},
sourceMap: false
}),
new webpack.BannerPlugin({
banner: banner,
entryOnly: true
})
]
},
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'].concat(
// It is guaranteed to exist because we tweak it in `env.js`
process.env.NODE_PATH.split(path.delimiter).filter(Boolean)
),
// 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',
},
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.appSrcLib, [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)$/,
enforce: 'pre',
use: [
{
options: {
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
},
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 just like "file" loader but it also embeds
// assets smaller than specified size as data URLs to avoid requests.
{
test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
loader: require.resolve('url-loader'),
options: {
limit: 10000,
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?-prettier,-svgo![path]',
},
},
},
],
],
cacheDirectory: true,
// Save disk space when time isn't as important
cacheCompression: true,
compact: true,
},
},
// 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 },
],
],
cacheDirectory: true,
// Save disk space when time isn't as important
cacheCompression: true,
// If an error happens in a package, it's possible to be
// because it was compiled. Thus, we don't want the browser
// debugger to show the original code. Instead, the code
// being evaluated would be much more helpful.
sourceMaps: false,
},
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// `MiniCSSExtractPlugin` extracts styles into CSS
// files. If you use code splitting, async bundles will have their own separate CSS chunk file.
// By default we support CSS Modules with the extension .module.css
{
test: cssRegex,
exclude: cssModuleRegex,
loader: getStyleLoaders({
importLoaders: 1,
sourceMap: 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,
loader: getStyleLoaders({
importLoaders: 1,
sourceMap: shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
}),
},
// Opt-in support for SASS. The logic here is somewhat similar
// as in the CSS routine, except that "sass-loader" runs first
// to compile SASS files into CSS.
// By default we support SASS Modules with the
// extensions .module.scss or .module.sass
{
test: sassRegex,
exclude: sassModuleRegex,
loader: getStyleLoaders(
{
importLoaders: 2,
sourceMap: 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,
loader: getStyleLoaders(
{
importLoaders: 2,
sourceMap: shouldUseSourceMap,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
'sass-loader'
),
},
// "file" loader makes sure assets end up in the `build` folder.
// When you `import` an asset, you get its filename.
// 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
// it's 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({
inject: true,
template: paths.appHtml,
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true,
},
}),*/
// Inlines the webpack runtime script. This script is too small to warrant
// a network request.
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="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
// In production, 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 was set to production here.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'css/simple-keyboard.css',
chunkFilename: 'css/index.[contenthash:8].chunk.css',
}),
// Generate a manifest file which contains a mapping of all asset filenames
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
/*new ManifestPlugin({
fileName: 'asset-manifest.json',
publicPath: publicPath,
}),*/
// 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.
/*new WorkboxWebpackPlugin.GenerateSW({
clientsClaim: true,
exclude: [/\.map$/, /asset-manifest\.json$/],
importWorkboxFrom: 'cdn',
navigateFallback: publicUrl + '/index.html',
navigateFallbackBlacklist: [
// Exclude URLs starting with /_, as they're likely an API call
new RegExp('^/_'),
// Exclude URLs containing a dot, as they're likely a resource in
// public/ and not a SPA route
new RegExp('/[^/]+\\.[^/]+$'),
],
}),*/
// TypeScript type checking
fs.existsSync(paths.appTsConfig) &&
new ForkTsCheckerWebpackPlugin({
typescript: resolve.sync('typescript', {
basedir: paths.appNodeModules,
}),
async: false,
checkSyntacticErrors: true,
tsconfig: paths.appTsConfig,
compilerOptions: {
module: 'esnext',
moduleResolution: 'node',
resolveJsonModule: true,
isolatedModules: true,
noEmit: true,
jsx: 'preserve',
},
reportFiles: [
'**',
'!**/*.json',
'!**/__tests__/**',
'!**/?(*.)(spec|test).*',
'!src/setupProxy.js',
'!src/setupTests.*',
],
watch: paths.appSrcLib,
silent: true,
formatter: typescriptFormatter,
}),
].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: {
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty',
},
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
};
+21 -16
View File
@@ -1,10 +1,12 @@
'use strict'; 'use strict';
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware'); const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware'); const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
const path = require('path'); const ignoredFiles = require('react-dev-utils/ignoredFiles');
const config = require('./webpack.config.dev'); const config = require('./webpack.config.dev');
const paths = require('./paths'); const paths = require('./paths');
const fs = require('fs');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const host = process.env.HOST || '0.0.0.0'; const host = process.env.HOST || '0.0.0.0';
@@ -17,8 +19,8 @@ module.exports = function(proxy, allowedHost) {
// https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a // https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
// However, it made several existing use cases such as development in cloud // However, it made several existing use cases such as development in cloud
// environment or subdomains in development significantly more complicated: // environment or subdomains in development significantly more complicated:
// https://github.com/facebookincubator/create-react-app/issues/2271 // https://github.com/facebook/create-react-app/issues/2271
// https://github.com/facebookincubator/create-react-app/issues/2233 // https://github.com/facebook/create-react-app/issues/2233
// While we're investigating better solutions, for now we will take a // While we're investigating better solutions, for now we will take a
// compromise. Since our WDS configuration only serves files in the `public` // compromise. Since our WDS configuration only serves files in the `public`
// folder we won't consider accessing them a vulnerability. However, if you // folder we won't consider accessing them a vulnerability. However, if you
@@ -61,39 +63,42 @@ module.exports = function(proxy, allowedHost) {
// as we specified in the config. In development, we always serve from /. // as we specified in the config. In development, we always serve from /.
publicPath: config.output.publicPath, publicPath: config.output.publicPath,
// WebpackDevServer is noisy by default so we emit custom message instead // WebpackDevServer is noisy by default so we emit custom message instead
// by listening to the compiler events with `compiler.plugin` calls above. // by listening to the compiler events with `compiler.hooks[...].tap` calls above.
quiet: true, quiet: true,
// Reportedly, this avoids CPU overload on some systems. // Reportedly, this avoids CPU overload on some systems.
// https://github.com/facebookincubator/create-react-app/issues/293 // https://github.com/facebook/create-react-app/issues/293
// src/node_modules is not ignored to support absolute imports // src/node_modules is not ignored to support absolute imports
// https://github.com/facebookincubator/create-react-app/issues/1065 // https://github.com/facebook/create-react-app/issues/1065
watchOptions: { watchOptions: {
ignored: new RegExp( ignored: ignoredFiles(paths.appSrc),
`^(?!${path
.normalize(paths.appSrc + '/')
.replace(/[\\]+/g, '\\\\')}).+[\\\\/]node_modules[\\\\/]`,
'g'
),
}, },
// Enable HTTPS if the HTTPS environment variable is set to 'true' // Enable HTTPS if the HTTPS environment variable is set to 'true'
https: protocol === 'https', https: protocol === 'https',
host: host, host,
overlay: false, overlay: false,
historyApiFallback: { historyApiFallback: {
// Paths with dots should still use the history fallback. // Paths with dots should still use the history fallback.
// See https://github.com/facebookincubator/create-react-app/issues/387. // See https://github.com/facebook/create-react-app/issues/387.
disableDotRule: true, disableDotRule: true,
}, },
public: allowedHost, public: allowedHost,
proxy, proxy,
before(app) { before(app, server) {
if (fs.existsSync(paths.proxySetup)) {
// This registers user provided middleware for proxy reasons
require(paths.proxySetup)(app);
}
// 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. // This lets us open files from the runtime error overlay.
app.use(errorOverlayMiddleware()); app.use(errorOverlayMiddleware());
// This service worker file is effectively a 'no-op' that will reset any // This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination. // previous service worker registered for the same host:port combination.
// We do this in development to avoid hitting the production cache if // We do this in development to avoid hitting the production cache if
// it used the same host and port. // it used the same host and port.
// https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432 // https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
app.use(noopServiceWorkerMiddleware()); app.use(noopServiceWorkerMiddleware());
}, },
}; };
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -175,8 +175,7 @@ This class:</p>
<div> <div>
<div data-ice="description"><p>Tracks current cursor position <div data-ice="description"><p>Only first instance should insall the caret handling events</p>
As keys are pressed, text will be added/removed at that position within the input.</p>
</div> </div>
</div> </div>
</td> </td>
@@ -1345,7 +1344,7 @@ As keys are pressed, text will be added/removed at that position within the inpu
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber121">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber123">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1388,15 +1387,14 @@ As keys are pressed, text will be added/removed at that position within the inpu
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber519">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber512">source</a></span></span>
</span> </span>
</h3> </h3>
<div data-ice="description"><p>Tracks current cursor position <div data-ice="description"><p>Only first instance should insall the caret handling events</p>
As keys are pressed, text will be added/removed at that position within the input.</p>
</div> </div>
@@ -1433,7 +1431,7 @@ As keys are pressed, text will be added/removed at that position within the inpu
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber597">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber634">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1477,7 +1475,7 @@ As keys are pressed, text will be added/removed at that position within the inpu
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber607">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber644">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1521,7 +1519,7 @@ As keys are pressed, text will be added/removed at that position within the inpu
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber265">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber274">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1564,7 +1562,7 @@ As keys are pressed, text will be added/removed at that position within the inpu
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber229">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber235">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1607,7 +1605,7 @@ As keys are pressed, text will be added/removed at that position within the inpu
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber744">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber798">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1651,7 +1649,7 @@ As keys are pressed, text will be added/removed at that position within the inpu
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber110">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber112">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1731,7 +1729,7 @@ keyboard.clearInput();</code></pre>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber218">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber224">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1818,7 +1816,7 @@ keyboard.clearInput();</code></pre>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber116">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber118">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1861,7 +1859,7 @@ keyboard.clearInput();</code></pre>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber69">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber71">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1904,7 +1902,7 @@ keyboard.clearInput();</code></pre>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber150">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber152">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1948,7 +1946,7 @@ keyboard.clearInput();</code></pre>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber61">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber62">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2065,6 +2063,13 @@ keyboard.clearInput();</code></pre>
<td data-ice="description"><p>Enable highlighting of keys pressed on physical keyboard.</p> <td data-ice="description"><p>Enable highlighting of keys pressed on physical keyboard.</p>
</td> </td>
</tr> </tr>
<tr data-ice="property" data-depth="0">
<td data-ice="name" class="code" data-depth="0">preventMouseDownDefault</td>
<td data-ice="type" class="code"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span></td>
<td data-ice="appendix"></td>
<td data-ice="description"><p>Calling preventDefault for the mousedown events keeps the focus on the input.</p>
</td>
</tr>
<tr data-ice="property" data-depth="0"> <tr data-ice="property" data-depth="0">
<td data-ice="name" class="code" data-depth="0">physicalKeyboardHighlightTextColor</td> <td data-ice="name" class="code" data-depth="0">physicalKeyboardHighlightTextColor</td>
<td data-ice="type" class="code"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span></td> <td data-ice="type" class="code"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span></td>
@@ -2148,7 +2153,7 @@ keyboard.clearInput();</code></pre>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber145">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber147">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2192,7 +2197,7 @@ keyboard.clearInput();</code></pre>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber563">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber600">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2236,7 +2241,7 @@ keyboard.clearInput();</code></pre>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber339">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber352">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2327,7 +2332,7 @@ keyboard.clearInput();</code></pre>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber375">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber388">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2407,7 +2412,7 @@ keyboard.clearInput();</code></pre>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber508">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber535">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2467,7 +2472,7 @@ keyboard.clearInput();</code></pre>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber349">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber362">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2512,7 +2517,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber284">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber297">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2573,7 +2578,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber359">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber372">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2646,7 +2651,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber477">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber490">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2719,7 +2724,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber299">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber312">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2792,7 +2797,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber158">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber160">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2865,7 +2870,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber261">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber267">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2925,7 +2930,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber214">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber220">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -2991,7 +2996,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber251">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber257">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -3035,7 +3040,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber495">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber508">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -3079,7 +3084,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber573">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber610">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -3123,7 +3128,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber530">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber567">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -3167,7 +3172,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber555">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber592">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -3211,7 +3216,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber547">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber584">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -3255,7 +3260,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber425">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber438">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -3335,7 +3340,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber614">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber651">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -3379,7 +3384,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber331">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber344">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -3440,7 +3445,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber316">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber329">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -3508,7 +3513,7 @@ Used interally between re-renders.</p>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber274">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/components/Keyboard.js.html#lineNumber287">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -198,7 +198,7 @@
<span class="code" data-ice="name"><span><a href="class/src/lib/services/Utilities.js~Utilities.html#instance-method-addStringAt">addStringAt</a></span></span><span class="code" data-ice="signature">(source: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, string: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, position: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>): <span>*</span></span> <span class="code" data-ice="name"><span><a href="class/src/lib/services/Utilities.js~Utilities.html#instance-method-addStringAt">addStringAt</a></span></span><span class="code" data-ice="signature">(source: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, string: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, position: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>, moveCaret: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span>): <span>*</span></span>
</p> </p>
</div> </div>
<div> <div>
@@ -372,7 +372,7 @@
<span class="code" data-ice="name"><span><a href="class/src/lib/services/Utilities.js~Utilities.html#instance-method-getUpdatedInput">getUpdatedInput</a></span></span><span class="code" data-ice="signature">(button: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, input: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, options: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span>, caretPos: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>): <span>*</span></span> <span class="code" data-ice="name"><span><a href="class/src/lib/services/Utilities.js~Utilities.html#instance-method-getUpdatedInput">getUpdatedInput</a></span></span><span class="code" data-ice="signature">(button: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, input: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, options: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span>, caretPos: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>, moveCaret: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span>): <span>*</span></span>
</p> </p>
</div> </div>
<div> <div>
@@ -459,7 +459,7 @@
<span class="code" data-ice="name"><span><a href="class/src/lib/services/Utilities.js~Utilities.html#instance-method-removeAt">removeAt</a></span></span><span class="code" data-ice="signature">(source: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, position: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>): <span>*</span></span> <span class="code" data-ice="name"><span><a href="class/src/lib/services/Utilities.js~Utilities.html#instance-method-removeAt">removeAt</a></span></span><span class="code" data-ice="signature">(source: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, position: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>, moveCaret: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span>): <span>*</span></span>
</p> </p>
</div> </div>
<div> <div>
@@ -501,6 +501,35 @@
<td> <td>
</td>
</tr>
<tr data-ice="target">
<td>
<span class="access" data-ice="access">public</span>
<span class="override" data-ice="override"></span>
</td>
<td>
<div>
<p>
<span class="code" data-ice="name"><span><a href="class/src/lib/services/Utilities.js~Utilities.html#instance-method-updateCaretPosAction">updateCaretPosAction</a></span></span><span class="code" data-ice="signature">(instance: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span>, length: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>, minus: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span>)</span>
</p>
</div>
<div>
<div data-ice="description"><p>Action method of updateCaretPos</p>
</div>
</div>
</td>
<td>
</td> </td>
</tr> </tr>
</tbody> </tbody>
@@ -572,7 +601,7 @@
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber300">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber322">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -657,11 +686,11 @@
<span class="code" data-ice="name">addStringAt</span><span class="code" data-ice="signature">(source: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, string: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, position: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>): <span>*</span></span> <span class="code" data-ice="name">addStringAt</span><span class="code" data-ice="signature">(source: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, string: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, position: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>, moveCaret: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span>): <span>*</span></span>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber198">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber224">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -702,6 +731,13 @@
<td data-ice="description"><p>The (cursor) position where the string should be added</p> <td data-ice="description"><p>The (cursor) position where the string should be added</p>
</td> </td>
</tr> </tr>
<tr data-ice="property" data-depth="0">
<td data-ice="name" class="code" data-depth="0">moveCaret</td>
<td data-ice="type" class="code"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span></td>
<td data-ice="appendix"></td>
<td data-ice="description"><p>Whether to update simple-keyboard&apos;s cursor</p>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -748,7 +784,7 @@
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber337">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber359">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -821,7 +857,7 @@
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber347">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber369">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -901,7 +937,7 @@
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber32">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber33">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -975,7 +1011,7 @@
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber116">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber117">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1062,7 +1098,7 @@
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber46">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber47">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1114,11 +1150,11 @@
<span class="code" data-ice="name">getUpdatedInput</span><span class="code" data-ice="signature">(button: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, input: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, options: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span>, caretPos: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>): <span>*</span></span> <span class="code" data-ice="name">getUpdatedInput</span><span class="code" data-ice="signature">(button: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, input: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, options: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span>, caretPos: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>, moveCaret: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span>): <span>*</span></span>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber135">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber137">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1166,6 +1202,13 @@
<td data-ice="description"><p>The cursor&apos;s current position</p> <td data-ice="description"><p>The cursor&apos;s current position</p>
</td> </td>
</tr> </tr>
<tr data-ice="property" data-depth="0">
<td data-ice="name" class="code" data-depth="0">moveCaret</td>
<td data-ice="type" class="code"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span></td>
<td data-ice="appendix"></td>
<td data-ice="description"><p>Whether to update simple-keyboard&apos;s cursor</p>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -1212,7 +1255,7 @@
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber275">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber298">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1296,7 +1339,7 @@
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber328">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber350">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1348,11 +1391,11 @@
<span class="code" data-ice="name">removeAt</span><span class="code" data-ice="signature">(source: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, position: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>): <span>*</span></span> <span class="code" data-ice="name">removeAt</span><span class="code" data-ice="signature">(source: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String">string</a></span>, position: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>, moveCaret: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span>): <span>*</span></span>
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber228">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber251">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1386,6 +1429,13 @@
<td data-ice="description"><p>The (cursor) position from where the characters should be removed</p> <td data-ice="description"><p>The (cursor) position from where the characters should be removed</p>
</td> </td>
</tr> </tr>
<tr data-ice="property" data-depth="0">
<td data-ice="name" class="code" data-depth="0">moveCaret</td>
<td data-ice="type" class="code"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span></td>
<td data-ice="appendix"></td>
<td data-ice="description"><p>Whether to update simple-keyboard&apos;s cursor</p>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@@ -1432,7 +1482,7 @@
<span class="right-info"> <span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber182">source</a></span></span> <span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber186">source</a></span></span>
</span> </span>
</h3> </h3>
@@ -1487,6 +1537,81 @@
</div>
<div class="detail" data-ice="detail">
<h3 data-ice="anchor" id="instance-method-updateCaretPosAction">
<span class="access" data-ice="access">public</span>
<span class="code" data-ice="name">updateCaretPosAction</span><span class="code" data-ice="signature">(instance: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span>, length: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span>, minus: <span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span>)</span>
<span class="right-info">
<span data-ice="source"><span><a href="file/src/lib/services/Utilities.js.html#lineNumber203">source</a></span></span>
</span>
</h3>
<div data-ice="description"><p>Action method of updateCaretPos</p>
</div>
<div data-ice="properties"><div data-ice="properties">
<h4 data-ice="title">Params:</h4>
<table class="params">
<thead>
<tr><td>Name</td><td>Type</td><td>Attribute</td><td>Description</td></tr>
</thead>
<tbody>
<tr data-ice="property" data-depth="0">
<td data-ice="name" class="code" data-depth="0">instance</td>
<td data-ice="type" class="code"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object">object</a></span></td>
<td data-ice="appendix"></td>
<td data-ice="description"><p>The instance whose position should be updated</p>
</td>
</tr>
<tr data-ice="property" data-depth="0">
<td data-ice="name" class="code" data-depth="0">length</td>
<td data-ice="type" class="code"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number">number</a></span></td>
<td data-ice="appendix"></td>
<td data-ice="description"><p>Represents by how many characters the input should be moved</p>
</td>
</tr>
<tr data-ice="property" data-depth="0">
<td data-ice="name" class="code" data-depth="0">minus</td>
<td data-ice="type" class="code"><span><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">boolean</a></span></td>
<td data-ice="appendix"></td>
<td data-ice="description"><p>Whether the cursor should be moved to the left or not.</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
+4 -4
View File
@@ -1,7 +1,7 @@
{ {
"coverage": "100%", "coverage": "100%",
"expectCount": 77, "expectCount": 78,
"actualCount": 77, "actualCount": 78,
"files": { "files": {
"src/demo/App.js": { "src/demo/App.js": {
"expectCount": 8, "expectCount": 8,
@@ -24,8 +24,8 @@
"undocumentLines": [] "undocumentLines": []
}, },
"src/lib/services/Utilities.js": { "src/lib/services/Utilities.js": {
"expectCount": 15, "expectCount": 16,
"actualCount": 15, "actualCount": 16,
"undocumentLines": [] "undocumentLines": []
}, },
"src/lib/tests/TestUtility.js": { "src/lib/tests/TestUtility.js": {
+2 -2
View File
@@ -68,7 +68,7 @@ class App {
* Creates a new simple-keyboard instance * Creates a new simple-keyboard instance
*/ */
this.keyboard = new Keyboard({ this.keyboard = new Keyboard({
//debug: true, debug: true,
layoutName: this.layoutName, layoutName: this.layoutName,
onChange: input =&gt; this.onChange(input), onChange: input =&gt; this.onChange(input),
onKeyPress: button =&gt; this.onKeyPress(button), onKeyPress: button =&gt; this.onKeyPress(button),
@@ -85,7 +85,7 @@ class App {
&lt;/div&gt; &lt;/div&gt;
`); `);
document.querySelector(&apos;.input&apos;).addEventListener(&apos;change&apos;, (event) =&gt; { document.querySelector(&apos;.input&apos;).addEventListener(&apos;input&apos;, (event) =&gt; {
this.keyboard.setInput(event.target.value); this.keyboard.setInput(event.target.value);
}); });
} }
+74 -18
View File
@@ -92,8 +92,9 @@ class SimpleKeyboard {
* @property {object} maxLength Restrains simple-keyboard&#x2019;s individual inputs to a certain length. This should be used in addition to the input element&#x2019;s maxlengthattribute. * @property {object} maxLength Restrains simple-keyboard&#x2019;s individual inputs to a certain length. This should be used in addition to the input element&#x2019;s maxlengthattribute.
* @property {boolean} syncInstanceInputs When set to true, this option synchronizes the internal input of every simple-keyboard instance. * @property {boolean} syncInstanceInputs When set to true, this option synchronizes the internal input of every simple-keyboard instance.
* @property {boolean} physicalKeyboardHighlight Enable highlighting of keys pressed on physical keyboard. * @property {boolean} physicalKeyboardHighlight Enable highlighting of keys pressed on physical keyboard.
* @property {boolean} preventMouseDownDefault Calling preventDefault for the mousedown events keeps the focus on the input.
* @property {string} physicalKeyboardHighlightTextColor Define the text color that the physical keyboard highlighted key should have. * @property {string} physicalKeyboardHighlightTextColor Define the text color that the physical keyboard highlighted key should have.
* @property {string} physicalKeyboardHighlightBgColor Define the background color that the physical keyboard highlighted key should have. * @property {string} physicalKeyboardHighlightBgColor Define the background color that the physical keyboard highlighted key should have.
* @property {function(button: string):string} onKeyPress Executes the callback function on key press. Returns button layout name (i.e.: &#x201C;{shift}&#x201D;). * @property {function(button: string):string} onKeyPress Executes the callback function on key press. Returns button layout name (i.e.: &#x201C;{shift}&#x201D;).
* @property {function(input: string):string} onChange Executes the callback function on input change. Returns the current input&#x2019;s string. * @property {function(input: string):string} onChange Executes the callback function on input change. Returns the current input&#x2019;s string.
* @property {function} onRender Executes the callback function every time simple-keyboard is rendered (e.g: when you change layouts). * @property {function} onRender Executes the callback function every time simple-keyboard is rendered (e.g: when you change layouts).
@@ -104,6 +105,7 @@ class SimpleKeyboard {
this.options.layoutName = this.options.layoutName || &quot;default&quot;; this.options.layoutName = this.options.layoutName || &quot;default&quot;;
this.options.theme = this.options.theme || &quot;hg-theme-default&quot;; this.options.theme = this.options.theme || &quot;hg-theme-default&quot;;
this.options.inputName = this.options.inputName || &quot;default&quot;; this.options.inputName = this.options.inputName || &quot;default&quot;;
this.options.preventMouseDownDefault = this.options.preventMouseDownDefault === false ? false : true;
/** /**
* @type {object} Classes identifying loaded plugins * @type {object} Classes identifying loaded plugins
@@ -215,7 +217,9 @@ class SimpleKeyboard {
if(!this.input[this.options.inputName]) if(!this.input[this.options.inputName])
this.input[this.options.inputName] = &apos;&apos;; this.input[this.options.inputName] = &apos;&apos;;
let updatedInput = this.utilities.getUpdatedInput(button, this.input[this.options.inputName], this.options, this.caretPosition); let updatedInput = this.utilities.getUpdatedInput(
button, this.input[this.options.inputName], this.options, this.caretPosition
);
if(this.input[this.options.inputName] !== updatedInput){ if(this.input[this.options.inputName] !== updatedInput){
@@ -226,7 +230,9 @@ class SimpleKeyboard {
return false; return false;
} }
this.input[this.options.inputName] = updatedInput; this.input[this.options.inputName] = this.utilities.getUpdatedInput(
button, this.input[this.options.inputName], this.options, this.caretPosition, true
);
if(debug) if(debug)
console.log(&apos;Input changed:&apos;, this.input); console.log(&apos;Input changed:&apos;, this.input);
@@ -301,12 +307,19 @@ class SimpleKeyboard {
*/ */
/* istanbul ignore next */ /* istanbul ignore next */
handleButtonHold(button){ handleButtonHold(button){
if(this.holdInteractionTimeout)
clearTimeout(this.holdInteractionTimeout);
/** /**
* @type {object} Timeout dictating the speed of key hold iterations * @type {object} Timeout dictating the speed of key hold iterations
*/ */
this.holdInteractionTimeout = setTimeout(() =&gt; { this.holdInteractionTimeout = setTimeout(() =&gt; {
this.handleButtonClicked(button); if(this.isMouseHold){
this.handleButtonHold(button); this.handleButtonClicked(button);
this.handleButtonHold(button);
} else {
clearTimeout(this.holdInteractionTimeout);
}
}, 100); }, 100);
} }
@@ -325,7 +338,7 @@ class SimpleKeyboard {
*/ */
clearInput(inputName){ clearInput(inputName){
inputName = inputName || this.options.inputName; inputName = inputName || this.options.inputName;
this.input[this.options.inputName] = &apos;&apos;; this.input[inputName] = &apos;&apos;;
/** /**
* Enforce syncInstanceInputs, if set * Enforce syncInstanceInputs, if set
@@ -535,35 +548,59 @@ class SimpleKeyboard {
* Retrieves the current cursor position within a input or textarea (if any) * Retrieves the current cursor position within a input or textarea (if any)
*/ */
handleCaret(){ handleCaret(){
/**
* Only first instance should insall the caret handling events
*/
this.caretPosition = null;
let simpleKeyboardInstances = window[&apos;SimpleKeyboardInstances&apos;];
if(
(
simpleKeyboardInstances &amp;&amp;
Object.keys(simpleKeyboardInstances)[0] === this.utilities.camelCase(this.keyboardDOMClass)
) ||
!simpleKeyboardInstances
){
if(this.options.debug){ if(this.options.debug){
console.log(&quot;Caret handling started&quot;); console.log(`Caret handling started (${this.keyboardDOMClass})`)
} }
document.addEventListener(&quot;keyup&quot;, this.caretEventHandler); document.addEventListener(&quot;keyup&quot;, this.caretEventHandler);
document.addEventListener(&quot;mouseup&quot;, this.caretEventHandler); document.addEventListener(&quot;mouseup&quot;, this.caretEventHandler);
document.addEventListener(&quot;touchend&quot;, this.caretEventHandler); document.addEventListener(&quot;touchend&quot;, this.caretEventHandler);
} }
}
/** /**
* Called by {@link handleCaret} when an event that warrants a cursor position update is triggered * Called by {@link handleCaret} when an event that warrants a cursor position update is triggered
*/ */
caretEventHandler(event){ caretEventHandler(event){
let targetTagName = event.target.tagName.toLowerCase(); let targetTagName;
if(event.target.tagName){
targetTagName = event.target.tagName.toLowerCase();
}
this.dispatch(instance =&gt; {
if(instance.isMouseHold){
instance.isMouseHold = false;
}
if( if(
targetTagName === &quot;textarea&quot; || (targetTagName === &quot;textarea&quot; ||
targetTagName === &quot;input&quot; targetTagName === &quot;input&quot;) &amp;&amp;
!instance.options.disableCaretPositioning
){ ){
/** /**
* Tracks current cursor position * Tracks current cursor position
* As keys are pressed, text will be added/removed at that position within the input. * As keys are pressed, text will be added/removed at that position within the input.
*/ */
this.caretPosition = event.target.selectionStart; instance.caretPosition = event.target.selectionStart;
if(this.options.debug){ if(instance.options.debug){
console.log(&apos;Caret at: &apos;, event.target.selectionStart, event.target.tagName.toLowerCase()); console.log(&apos;Caret at: &apos;, event.target.selectionStart, event.target.tagName.toLowerCase(), `(${instance.keyboardDOMClass})`);
} }
} }
});
} }
/** /**
@@ -571,7 +608,7 @@ class SimpleKeyboard {
*/ */
onInit(){ onInit(){
if(this.options.debug){ if(this.options.debug){
console.log(&quot;Initialized&quot;); console.log(`${this.keyboardDOMClass} Initialized`)
} }
/** /**
@@ -661,6 +698,7 @@ class SimpleKeyboard {
let layoutClass = this.options.layout ? &quot;hg-layout-custom&quot; : `hg-layout-${this.options.layoutName}`; let layoutClass = this.options.layout ? &quot;hg-layout-custom&quot; : `hg-layout-${this.options.layoutName}`;
let layout = this.options.layout || KeyboardLayout.getDefaultLayout(); let layout = this.options.layout || KeyboardLayout.getDefaultLayout();
let useTouchEvents = this.options.useTouchEvents || false
/** /**
* Account for buttonTheme, if set * Account for buttonTheme, if set
@@ -726,9 +764,25 @@ class SimpleKeyboard {
*/ */
var buttonDOM = document.createElement(&apos;div&apos;); var buttonDOM = document.createElement(&apos;div&apos;);
buttonDOM.className += `hg-button ${fctBtnClass}${buttonThemeClass ? &quot; &quot;+buttonThemeClass : &quot;&quot;}`; buttonDOM.className += `hg-button ${fctBtnClass}${buttonThemeClass ? &quot; &quot;+buttonThemeClass : &quot;&quot;}`;
buttonDOM.onclick = () =&gt; this.handleButtonClicked(button);
buttonDOM.onmousedown = (e) =&gt; this.handleButtonMouseDown(button, e); if (useTouchEvents) {
buttonDOM.ontouchstart = (e) =&gt; {
this.handleButtonClicked(button);
this.handleButtonMouseDown(button, e);
}
buttonDOM.ontouchend = e =&gt; this.handleButtonMouseUp();
buttonDOM.ontouchcancel = e =&gt; this.handleButtonMouseUp();
} else {
buttonDOM.onclick = () =&gt; {
this.isMouseHold = false;
this.handleButtonClicked(button);
}
buttonDOM.onmousedown = (e) =&gt; {
if (this.options.preventMouseDownDefault) e.preventDefault();
this.handleButtonMouseDown(button, e);
}
}
/** /**
* Adding identifier * Adding identifier
*/ */
@@ -788,7 +842,9 @@ class SimpleKeyboard {
/** /**
* Handling mouseup * Handling mouseup
*/ */
document.onmouseup = () =&gt; this.handleButtonMouseUp(); if (!useTouchEvents) {
document.onmouseup = () =&gt; this.handleButtonMouseUp();
}
/** /**
* Calling onInit * Calling onInit
@@ -93,7 +93,7 @@ class PhysicalKeyboard {
this.simpleKeyboardInstance.dispatch(instance =&gt; { this.simpleKeyboardInstance.dispatch(instance =&gt; {
let buttonDOM = instance.getButtonElement(buttonPressed) || instance.getButtonElement(`{${buttonPressed}}`); let buttonDOM = instance.getButtonElement(buttonPressed) || instance.getButtonElement(`{${buttonPressed}}`);
if(buttonDOM){ if(buttonDOM &amp;&amp; buttonDOM.removeAttribute){
buttonDOM.removeAttribute(&quot;style&quot;); buttonDOM.removeAttribute(&quot;style&quot;);
} }
}); });
+50 -29
View File
@@ -60,6 +60,7 @@ class Utilities {
this.getButtonDisplayName = this.getButtonDisplayName.bind(this); this.getButtonDisplayName = this.getButtonDisplayName.bind(this);
this.getUpdatedInput = this.getUpdatedInput.bind(this); this.getUpdatedInput = this.getUpdatedInput.bind(this);
this.updateCaretPos = this.updateCaretPos.bind(this); this.updateCaretPos = this.updateCaretPos.bind(this);
this.updateCaretPosAction = this.updateCaretPosAction.bind(this);
this.isMaxLengthReached = this.isMaxLengthReached.bind(this); this.isMaxLengthReached = this.isMaxLengthReached.bind(this);
this.camelCase = this.camelCase.bind(this); this.camelCase = this.camelCase.bind(this);
this.countInArray = this.countInArray.bind(this); this.countInArray = this.countInArray.bind(this);
@@ -173,48 +174,51 @@ class Utilities {
* @param {string} input The input string * @param {string} input The input string
* @param {object} options The simple-keyboard options object * @param {object} options The simple-keyboard options object
* @param {number} caretPos The cursor&apos;s current position * @param {number} caretPos The cursor&apos;s current position
* @param {boolean} moveCaret Whether to update simple-keyboard&apos;s cursor
*/ */
getUpdatedInput(button, input, options, caretPos){ getUpdatedInput(button, input, options, caretPos, moveCaret){
let output = input; let output = input;
if((button === &quot;{bksp}&quot; || button === &quot;{backspace}&quot;) &amp;&amp; output.length &gt; 0){ if((button === &quot;{bksp}&quot; || button === &quot;{backspace}&quot;) &amp;&amp; output.length &gt; 0){
output = this.removeAt(output, caretPos); output = this.removeAt(output, caretPos, moveCaret);
} else if(button === &quot;{space}&quot;) } else if(button === &quot;{space}&quot;)
output = this.addStringAt(output, &quot; &quot;, caretPos); output = this.addStringAt(output, &quot; &quot;, caretPos, moveCaret);
else if(button === &quot;{tab}&quot; &amp;&amp; !(typeof options.tabCharOnTab === &quot;boolean&quot; &amp;&amp; options.tabCharOnTab === false)){ else if(button === &quot;{tab}&quot; &amp;&amp; !(typeof options.tabCharOnTab === &quot;boolean&quot; &amp;&amp; options.tabCharOnTab === false)){
output = this.addStringAt(output, &quot;\t&quot;, caretPos); output = this.addStringAt(output, &quot;\t&quot;, caretPos, moveCaret);
} else if((button === &quot;{enter}&quot; || button === &quot;{numpadenter}&quot;) &amp;&amp; options.newLineOnEnter) } else if((button === &quot;{enter}&quot; || button === &quot;{numpadenter}&quot;) &amp;&amp; options.newLineOnEnter)
output = this.addStringAt(output, &quot;\n&quot;, caretPos); output = this.addStringAt(output, &quot;\n&quot;, caretPos, moveCaret);
else if(button.includes(&quot;numpad&quot;) &amp;&amp; Number.isInteger(Number(button[button.length - 2]))){ else if(button.includes(&quot;numpad&quot;) &amp;&amp; Number.isInteger(Number(button[button.length - 2]))){
output = this.addStringAt(output, button[button.length - 2], caretPos); output = this.addStringAt(output, button[button.length - 2], caretPos);
} }
else if(button === &quot;{numpaddivide}&quot;) else if(button === &quot;{numpaddivide}&quot;)
output = this.addStringAt(output, &apos;/&apos;, caretPos); output = this.addStringAt(output, &apos;/&apos;, caretPos, moveCaret);
else if(button === &quot;{numpadmultiply}&quot;) else if(button === &quot;{numpadmultiply}&quot;)
output = this.addStringAt(output, &apos;*&apos;, caretPos); output = this.addStringAt(output, &apos;*&apos;, caretPos, moveCaret);
else if(button === &quot;{numpadsubtract}&quot;) else if(button === &quot;{numpadsubtract}&quot;)
output = this.addStringAt(output, &apos;-&apos;, caretPos); output = this.addStringAt(output, &apos;-&apos;, caretPos, moveCaret);
else if(button === &quot;{numpadadd}&quot;) else if(button === &quot;{numpadadd}&quot;)
output = this.addStringAt(output, &apos;+&apos;, caretPos); output = this.addStringAt(output, &apos;+&apos;, caretPos, moveCaret);
else if(button === &quot;{numpaddecimal}&quot;) else if(button === &quot;{numpaddecimal}&quot;)
output = this.addStringAt(output, &apos;.&apos;, caretPos); output = this.addStringAt(output, &apos;.&apos;, caretPos, moveCaret);
else if(button === &quot;{&quot; || button === &quot;}&quot;) else if(button === &quot;{&quot; || button === &quot;}&quot;)
output = this.addStringAt(output, button, caretPos); output = this.addStringAt(output, button, caretPos, moveCaret);
else if(!button.includes(&quot;{&quot;) &amp;&amp; !button.includes(&quot;}&quot;)) else if(!button.includes(&quot;{&quot;) &amp;&amp; !button.includes(&quot;}&quot;))
output = this.addStringAt(output, button, caretPos); output = this.addStringAt(output, button, caretPos, moveCaret);
return output; return output;
} }
/** /**
* Moves the cursor position by a given amount * Moves the cursor position by a given amount
* *
@@ -222,11 +226,32 @@ class Utilities {
* @param {boolean} minus Whether the cursor should be moved to the left or not. * @param {boolean} minus Whether the cursor should be moved to the left or not.
*/ */
updateCaretPos(length, minus){ updateCaretPos(length, minus){
if(minus){ if(this.simpleKeyboardInstance.options.syncInstanceInputs){
if(this.simpleKeyboardInstance.caretPosition &gt; 0) this.simpleKeyboardInstance.dispatch(instance =&gt; {
this.simpleKeyboardInstance.caretPosition = this.simpleKeyboardInstance.caretPosition - length this.updateCaretPosAction(instance, length, minus);
});
} else { } else {
this.simpleKeyboardInstance.caretPosition = this.simpleKeyboardInstance.caretPosition + length; this.updateCaretPosAction(this.simpleKeyboardInstance, length, minus);
}
}
/**
* Action method of updateCaretPos
*
* @param {object} instance The instance whose position should be updated
* @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(instance, length, minus){
if(minus){
if(instance.caretPosition &gt; 0)
instance.caretPosition = instance.caretPosition - length;
} else {
instance.caretPosition = instance.caretPosition + length;
}
if(this.simpleKeyboardInstance.options.debug){
console.log(&quot;Caret at:&quot;, instance.caretPosition, `(${instance.keyboardDOMClass})`);
} }
} }
@@ -236,14 +261,11 @@ class Utilities {
* @param {string} source The source input * @param {string} source The source input
* @param {string} string The string to add * @param {string} string The string to add
* @param {number} position The (cursor) position where the string should be added * @param {number} position The (cursor) position where the string should be added
* @param {boolean} moveCaret Whether to update simple-keyboard&apos;s cursor
*/ */
addStringAt(source, string, position){ addStringAt(source, string, position, moveCaret){
let output; let output;
if(this.simpleKeyboardInstance.options.debug){
console.log(&quot;Caret at:&quot;, position);
}
if(!position &amp;&amp; position !== 0){ if(!position &amp;&amp; position !== 0){
output = source + string; output = source + string;
} else { } else {
@@ -253,7 +275,7 @@ class Utilities {
* Avoid caret position change when maxLength is set * Avoid caret position change when maxLength is set
*/ */
if(!this.isMaxLengthReached()){ if(!this.isMaxLengthReached()){
this.updateCaretPos(string.length); if(moveCaret) this.updateCaretPos(string.length);
} }
} }
@@ -266,8 +288,9 @@ class Utilities {
* *
* @param {string} source The source input * @param {string} source The source input
* @param {number} position The (cursor) position from where the characters should be removed * @param {number} position The (cursor) position from where the characters should be removed
* @param {boolean} moveCaret Whether to update simple-keyboard&apos;s cursor
*/ */
removeAt(source, position){ removeAt(source, position, moveCaret){
if(this.simpleKeyboardInstance.caretPosition === 0){ if(this.simpleKeyboardInstance.caretPosition === 0){
return source; return source;
} }
@@ -287,10 +310,10 @@ class Utilities {
if(emojiMatched){ if(emojiMatched){
output = source.substr(0, (position - 2)) + source.substr(position); output = source.substr(0, (position - 2)) + source.substr(position);
this.updateCaretPos(2, true); if(moveCaret) this.updateCaretPos(2, true);
} else { } else {
output = source.substr(0, (position - 1)) + source.substr(position); output = source.substr(0, (position - 1)) + source.substr(position);
this.updateCaretPos(1, true); if(moveCaret) this.updateCaretPos(1, true);
} }
} else { } else {
prevTwoChars = source.slice(-2); prevTwoChars = source.slice(-2);
@@ -298,10 +321,10 @@ class Utilities {
if(emojiMatched){ if(emojiMatched){
output = source.slice(0, -2); output = source.slice(0, -2);
this.updateCaretPos(2, true); if(moveCaret) this.updateCaretPos(2, true);
} else { } else {
output = source.slice(0, -1); output = source.slice(0, -1);
this.updateCaretPos(1, true); if(moveCaret) this.updateCaretPos(1, true);
} }
} }
@@ -319,7 +342,6 @@ class Utilities {
let currentInput = inputObj[options.inputName]; let currentInput = inputObj[options.inputName];
let condition = currentInput.length === maxLength; let condition = currentInput.length === maxLength;
if( if(
/** /**
* If pressing this button won&apos;t add more characters * If pressing this button won&apos;t add more characters
@@ -389,7 +411,6 @@ class Utilities {
countInArray(array, value){ countInArray(array, value){
return array.reduce((n, x) =&gt; n + (x === value), 0); return array.reduce((n, x) =&gt; n + (x === value), 0);
} }
} }
export default Utilities;</code></pre> export default Utilities;</code></pre>
+189 -103
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -755,6 +755,12 @@ window.esdocSearchIndex = [
"src/lib/services/Utilities.js~Utilities#updateCaretPos", "src/lib/services/Utilities.js~Utilities#updateCaretPos",
"method" "method"
], ],
[
"src/lib/services/utilities.js~utilities#updatecaretposaction",
"class/src/lib/services/Utilities.js~Utilities.html#instance-method-updateCaretPosAction",
"src/lib/services/Utilities.js~Utilities#updateCaretPosAction",
"method"
],
[ [
"src/lib/tests/testutility.js", "src/lib/tests/testutility.js",
"file/src/lib/tests/TestUtility.js.html", "file/src/lib/tests/TestUtility.js.html",
+16 -16
View File
@@ -39,7 +39,7 @@
</div> </div>
</nav> </nav>
<div class="content" data-ice="content"><h1>Source <img data-ice="coverageBadge" src="./badge.svg"><span data-ice="totalCoverageCount" class="total-coverage-count">77/77</span></h1> <div class="content" data-ice="content"><h1>Source <img data-ice="coverageBadge" src="./badge.svg"><span data-ice="totalCoverageCount" class="total-coverage-count">78/78</span></h1>
<table class="files-summary" data-ice="files" data-use-coverage="true"> <table class="files-summary" data-ice="files" data-use-coverage="true">
<thead> <thead>
@@ -58,9 +58,9 @@
<td data-ice="filePath"><span><a href="file/src/demo/App.js.html">src/demo/App.js</a></span></td> <td data-ice="filePath"><span><a href="file/src/demo/App.js.html">src/demo/App.js</a></span></td>
<td data-ice="identifier" class="identifiers"><span><a href="class/src/demo/App.js~App.html">App</a></span></td> <td data-ice="identifier" class="identifiers"><span><a href="class/src/demo/App.js~App.html">App</a></span></td>
<td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">8/8</span></td> <td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">8/8</span></td>
<td style="display: none;" data-ice="size">1921 byte</td> <td style="display: none;" data-ice="size">1918 byte</td>
<td style="display: none;" data-ice="lines">84</td> <td style="display: none;" data-ice="lines">84</td>
<td style="display: none;" data-ice="updated">2018-11-05 16:32:42 (UTC)</td> <td style="display: none;" data-ice="updated">2018-11-01 01:59:07 (UTC)</td>
</tr> </tr>
<tr data-ice="file"> <tr data-ice="file">
<td data-ice="filePath"><span><a href="file/src/demo/index.js.html">src/demo/index.js</a></span></td> <td data-ice="filePath"><span><a href="file/src/demo/index.js.html">src/demo/index.js</a></span></td>
@@ -68,15 +68,15 @@
<td class="coverage"><span data-ice="coverage">-</span></td> <td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">70 byte</td> <td style="display: none;" data-ice="size">70 byte</td>
<td style="display: none;" data-ice="lines">5</td> <td style="display: none;" data-ice="lines">5</td>
<td style="display: none;" data-ice="updated">2018-09-02 03:31:31 (UTC)</td> <td style="display: none;" data-ice="updated">2018-11-06 21:41:50 (UTC)</td>
</tr> </tr>
<tr data-ice="file"> <tr data-ice="file">
<td data-ice="filePath"><span><a href="file/src/lib/components/Keyboard.js.html">src/lib/components/Keyboard.js</a></span></td> <td data-ice="filePath"><span><a href="file/src/lib/components/Keyboard.js.html">src/lib/components/Keyboard.js</a></span></td>
<td data-ice="identifier" class="identifiers"><span><a href="class/src/lib/components/Keyboard.js~SimpleKeyboard.html">SimpleKeyboard</a></span></td> <td data-ice="identifier" class="identifiers"><span><a href="class/src/lib/components/Keyboard.js~SimpleKeyboard.html">SimpleKeyboard</a></span></td>
<td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">41/41</span></td> <td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">41/41</span></td>
<td style="display: none;" data-ice="size">24443 byte</td> <td style="display: none;" data-ice="size">26391 byte</td>
<td style="display: none;" data-ice="lines">759</td> <td style="display: none;" data-ice="lines">815</td>
<td style="display: none;" data-ice="updated">2018-11-05 16:42:42 (UTC)</td> <td style="display: none;" data-ice="updated">2018-11-01 01:52:55 (UTC)</td>
</tr> </tr>
<tr data-ice="file"> <tr data-ice="file">
<td data-ice="filePath"><span><a href="file/src/lib/index.js.html">src/lib/index.js</a></span></td> <td data-ice="filePath"><span><a href="file/src/lib/index.js.html">src/lib/index.js</a></span></td>
@@ -84,7 +84,7 @@
<td class="coverage"><span data-ice="coverage">-</span></td> <td class="coverage"><span data-ice="coverage">-</span></td>
<td style="display: none;" data-ice="size">85 byte</td> <td style="display: none;" data-ice="size">85 byte</td>
<td style="display: none;" data-ice="lines">2</td> <td style="display: none;" data-ice="lines">2</td>
<td style="display: none;" data-ice="updated">2018-06-06 23:02:53 (UTC)</td> <td style="display: none;" data-ice="updated">2018-11-06 21:41:50 (UTC)</td>
</tr> </tr>
<tr data-ice="file"> <tr data-ice="file">
<td data-ice="filePath"><span><a href="file/src/lib/services/KeyboardLayout.js.html">src/lib/services/KeyboardLayout.js</a></span></td> <td data-ice="filePath"><span><a href="file/src/lib/services/KeyboardLayout.js.html">src/lib/services/KeyboardLayout.js</a></span></td>
@@ -92,23 +92,23 @@
<td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">2/2</span></td> <td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">2/2</span></td>
<td style="display: none;" data-ice="size">746 byte</td> <td style="display: none;" data-ice="size">746 byte</td>
<td style="display: none;" data-ice="lines">28</td> <td style="display: none;" data-ice="lines">28</td>
<td style="display: none;" data-ice="updated">2018-10-03 02:14:58 (UTC)</td> <td style="display: none;" data-ice="updated">2018-11-06 21:41:48 (UTC)</td>
</tr> </tr>
<tr data-ice="file"> <tr data-ice="file">
<td data-ice="filePath"><span><a href="file/src/lib/services/PhysicalKeyboard.js.html">src/lib/services/PhysicalKeyboard.js</a></span></td> <td data-ice="filePath"><span><a href="file/src/lib/services/PhysicalKeyboard.js.html">src/lib/services/PhysicalKeyboard.js</a></span></td>
<td data-ice="identifier" class="identifiers"><span><a href="class/src/lib/services/PhysicalKeyboard.js~PhysicalKeyboard.html">PhysicalKeyboard</a></span></td> <td data-ice="identifier" class="identifiers"><span><a href="class/src/lib/services/PhysicalKeyboard.js~PhysicalKeyboard.html">PhysicalKeyboard</a></span></td>
<td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">5/5</span></td> <td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">5/5</span></td>
<td style="display: none;" data-ice="size">2879 byte</td> <td style="display: none;" data-ice="size">2908 byte</td>
<td style="display: none;" data-ice="lines">96</td> <td style="display: none;" data-ice="lines">96</td>
<td style="display: none;" data-ice="updated">2018-10-03 02:15:56 (UTC)</td> <td style="display: none;" data-ice="updated">2018-11-06 21:41:48 (UTC)</td>
</tr> </tr>
<tr data-ice="file"> <tr data-ice="file">
<td data-ice="filePath"><span><a href="file/src/lib/services/Utilities.js.html">src/lib/services/Utilities.js</a></span></td> <td data-ice="filePath"><span><a href="file/src/lib/services/Utilities.js.html">src/lib/services/Utilities.js</a></span></td>
<td data-ice="identifier" class="identifiers"><span><a href="class/src/lib/services/Utilities.js~Utilities.html">Utilities</a></span></td> <td data-ice="identifier" class="identifiers"><span><a href="class/src/lib/services/Utilities.js~Utilities.html">Utilities</a></span></td>
<td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">15/15</span></td> <td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">16/16</span></td>
<td style="display: none;" data-ice="size">10567 byte</td> <td style="display: none;" data-ice="size">11708 byte</td>
<td style="display: none;" data-ice="lines">352</td> <td style="display: none;" data-ice="lines">373</td>
<td style="display: none;" data-ice="updated">2018-10-03 17:11:36 (UTC)</td> <td style="display: none;" data-ice="updated">2018-11-01 01:55:18 (UTC)</td>
</tr> </tr>
<tr data-ice="file"> <tr data-ice="file">
<td data-ice="filePath"><span><a href="file/src/lib/tests/TestUtility.js.html">src/lib/tests/TestUtility.js</a></span></td> <td data-ice="filePath"><span><a href="file/src/lib/tests/TestUtility.js.html">src/lib/tests/TestUtility.js</a></span></td>
@@ -116,7 +116,7 @@
<td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">6/6</span></td> <td class="coverage"><span data-ice="coverage">100 %</span><span data-ice="coverageCount" class="coverage-count">6/6</span></td>
<td style="display: none;" data-ice="size">2830 byte</td> <td style="display: none;" data-ice="size">2830 byte</td>
<td style="display: none;" data-ice="lines">106</td> <td style="display: none;" data-ice="lines">106</td>
<td style="display: none;" data-ice="updated">2018-10-04 22:12:09 (UTC)</td> <td style="display: none;" data-ice="updated">2018-11-06 21:41:47 (UTC)</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
+10441 -6118
View File
File diff suppressed because it is too large Load Diff
+101 -63
View File
@@ -1,15 +1,16 @@
{ {
"name": "simple-keyboard", "name": "simple-keyboard",
"version": "2.7.7", "version": "2.16.0",
"description": "On-screen Javascript Virtual Keyboard", "description": "On-screen Javascript Virtual Keyboard",
"main": "build/index.js", "main": "build/index.js",
"types": "build/index.d.ts",
"scripts": { "scripts": {
"start": "node scripts/start.js", "start": "node scripts/start.js",
"build": "node scripts/build.js", "build": "node scripts/build.js && node scripts/buildUnminified.js",
"demo": "node scripts/demo.js", "demo": "node scripts/demo.js",
"test": "node scripts/test.js --env=jsdom", "test": "node scripts/test.js",
"postinstall": "node bin/postinstall", "postinstall": "node bin/postinstall",
"prepublish": "npm run build", "prepare": "npm run build",
"docs": "esdoc" "docs": "esdoc"
}, },
"repository": { "repository": {
@@ -34,87 +35,121 @@
"touchscreen", "touchscreen",
"touch-screen", "touch-screen",
"kiosk", "kiosk",
"osk" "osk",
"js"
], ],
"license": "MIT", "license": "MIT",
"dependencies": {},
"devDependencies": { "devDependencies": {
"autoprefixer": "7.1.6", "@babel/core": "7.3.4",
"babel-core": "^6.26.3", "@babel/plugin-proposal-class-properties": "^7.3.4",
"babel-eslint": "7.2.3", "@babel/preset-env": "^7.3.4",
"babel-jest": "20.0.3", "@babel/preset-react": "^7.0.0",
"babel-loader": "7.1.2", "@svgr/webpack": "4.1.0",
"babel-preset-react-app": "^3.1.2", "babel-core": "7.0.0-bridge.0",
"babel-runtime": "6.26.0", "babel-eslint": "10.0.1",
"case-sensitive-paths-webpack-plugin": "2.1.1", "babel-jest": "23.6.0",
"chalk": "1.1.3", "babel-loader": "8.0.5",
"copy-webpack-plugin": "^4.3.1", "babel-plugin-named-asset-import": "^0.3.1",
"css-loader": "0.28.7", "babel-preset-react-app": "^7.0.1",
"dotenv": "4.0.0", "bfj": "6.1.1",
"case-sensitive-paths-webpack-plugin": "2.2.0",
"chalk": "2.4.2",
"copy-webpack-plugin": "^5.0.0",
"css-loader": "2.1.0",
"dotenv": "6.2.0",
"dotenv-expand": "4.2.0",
"esdoc": "^1.1.0", "esdoc": "^1.1.0",
"esdoc-ecmascript-proposal-plugin": "^1.0.0", "esdoc-ecmascript-proposal-plugin": "^1.0.0",
"esdoc-standard-plugin": "^1.0.0", "esdoc-standard-plugin": "^1.0.0",
"eslint": "4.10.0", "eslint": "5.15.0",
"eslint-config-react-app": "^2.0.1", "eslint-config-react-app": "^3.0.7",
"eslint-loader": "1.9.0", "eslint-loader": "2.1.2",
"eslint-plugin-flowtype": "2.39.1", "eslint-plugin-flowtype": "3.4.2",
"eslint-plugin-import": "2.8.0", "eslint-plugin-import": "2.16.0",
"eslint-plugin-jsx-a11y": "5.1.1", "eslint-plugin-jsx-a11y": "6.2.1",
"eslint-plugin-react": "7.4.0", "eslint-plugin-react": "7.12.4",
"extract-text-webpack-plugin": "3.0.2", "file-loader": "3.0.1",
"file-loader": "1.1.5", "fork-ts-checker-webpack-plugin-alt": "0.4.14",
"fs-extra": "3.0.1", "fs-extra": "7.0.1",
"html-webpack-plugin": "2.29.0", "html-webpack-plugin": "4.0.0-beta.5",
"istanbul": "^0.4.5", "identity-obj-proxy": "3.0.0",
"istanbul-api": "^2.0.6", "jest": "23.6.0",
"istanbul-reports": "^2.0.1", "jest-pnp-resolver": "1.0.2",
"jest": "20.0.4", "jest-resolve": "23.6.0",
"object-assign": "4.1.1", "mini-css-extract-plugin": "0.5.0",
"postcss-flexbugs-fixes": "3.2.0", "optimize-css-assets-webpack-plugin": "5.0.1",
"postcss-loader": "2.0.8", "pnp-webpack-plugin": "1.3.1",
"promise": "8.0.1", "postcss-flexbugs-fixes": "4.1.0",
"prop-types": "^15.6.1", "postcss-loader": "3.0.0",
"raf": "3.4.0", "postcss-preset-env": "6.6.0",
"react": "^16.2.0", "postcss-safe-parser": "4.0.1",
"react-dev-utils": "^4.2.2", "prettier": "^1.16.4",
"react-dom": "^16.2.0", "prettier-webpack-plugin": "^1.2.0",
"style-loader": "0.19.0", "react": "^16.8.3",
"sw-precache-webpack-plugin": "0.11.4", "react-app-polyfill": "^0.2.1",
"url-loader": "0.6.2", "react-dev-utils": "^7.0.3",
"webpack": "3.8.1", "react-dom": "^16.8.3",
"webpack-dev-server": "2.9.4", "resolve": "1.10.0",
"webpack-manifest-plugin": "1.3.2", "sass-loader": "7.1.0",
"whatwg-fetch": "2.0.3" "style-loader": "0.23.1",
"terser-webpack-plugin": "1.2.3",
"uglifyjs-webpack-plugin": "^2.1.2",
"url-loader": "1.1.2",
"webpack": "4.29.6",
"webpack-dev-server": "3.2.1",
"webpack-manifest-plugin": "2.0.4",
"workbox-webpack-plugin": "4.0.0"
}, },
"eslintConfig": {
"extends": "react-app",
"settings": {
"react": {
"pragma": "React",
"version": "detect"
}
}
},
"browserslist": [
">0.2%",
"not ie <= 11",
"not op_mini all"
],
"jest": { "jest": {
"collectCoverageFrom": [ "collectCoverageFrom": [
"src/**/*.{js,jsx,mjs}", "src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts",
"!**/tests/**" "!**/tests/**"
], ],
"resolver": "jest-pnp-resolver",
"setupFiles": [ "setupFiles": [
"<rootDir>/config/polyfills.js" "react-app-polyfill/jsdom"
], ],
"testMatch": [ "testMatch": [
"<rootDir>/src/**/__tests__/**/*.{js,jsx,mjs}", "<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
"<rootDir>/src/**/?(*.)(spec|test).{js,jsx,mjs}" "<rootDir>/src/**/?(*.)(spec|test).{js,jsx,ts,tsx}"
], ],
"testEnvironment": "node", "testEnvironment": "jsdom",
"testURL": "http://localhost", "testURL": "http://localhost",
"transform": { "transform": {
"^.+\\.(js|jsx|mjs)$": "<rootDir>/node_modules/babel-jest", "^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js", "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
"^(?!.*\\.(js|jsx|mjs|css|json)$)": "<rootDir>/config/jest/fileTransform.js" "^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
}, },
"transformIgnorePatterns": [ "transformIgnorePatterns": [
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$" "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
"^.+\\.module\\.(css|sass|scss)$"
], ],
"moduleNameMapper": { "moduleNameMapper": {
"^react-native$": "react-native-web" "^react-native$": "react-native-web",
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
}, },
"moduleFileExtensions": [ "moduleFileExtensions": [
"web.js", "web.js",
"mjs",
"js", "js",
"web.ts",
"ts",
"web.tsx",
"tsx",
"json", "json",
"web.jsx", "web.jsx",
"jsx", "jsx",
@@ -123,10 +158,13 @@
}, },
"babel": { "babel": {
"presets": [ "presets": [
"react-app" "@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-proposal-class-properties"
]
] ]
},
"eslintConfig": {
"extends": "react-app"
} }
} }
+72 -10
View File
@@ -14,37 +14,55 @@ process.on('unhandledRejection', err => {
// Ensure environment variables are read. // Ensure environment variables are read.
require('../config/env'); require('../config/env');
const path = require('path');
const chalk = require('chalk'); const chalk = require('chalk');
const fs = require('fs-extra'); const fs = require('fs-extra');
const webpack = require('webpack'); const webpack = require('webpack');
const bfj = require('bfj');
const config = require('../config/webpack.config.prod'); const config = require('../config/webpack.config.prod');
const paths = require('../config/paths'); const paths = require('../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter'); const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const printBuildError = require('react-dev-utils/printBuildError'); const printBuildError = require('react-dev-utils/printBuildError');
const measureFileSizesBeforeBuild = const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild; FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);
// These sizes are pretty large. We'll warn for bundles exceeding them. // These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing // Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appLibIndexJs])) { // CRL: Updated with library index file if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1); process.exit(1);
} }
// First, read the current file sizes in build directory. // Process CLI arguments
// This lets us display how much they changed later. const argv = process.argv.slice(2);
measureFileSizesBeforeBuild(paths.appBuild) const writeStatsJson = argv.indexOf('--stats') !== -1;
// 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 => { .then(previousFileSizes => {
// Remove all content but keep the directory so that // Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash // if you're in it, you don't end up in Trash
fs.emptyDirSync(paths.appBuild); fs.emptyDirSync(paths.appBuild);
// Merge with the public folder
//copyPublicFolder();
// Start the webpack build // Start the webpack build
return build(previousFileSizes); return build(previousFileSizes);
}) })
@@ -76,13 +94,31 @@ measureFileSizesBeforeBuild(paths.appBuild)
WARN_AFTER_CHUNK_GZIP_SIZE WARN_AFTER_CHUNK_GZIP_SIZE
); );
console.log(); console.log();
const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrl;
const publicPath = config.output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
appPackage,
publicUrl,
publicPath,
buildFolder,
useYarn
);
}, },
err => { err => {
console.log(chalk.red('Failed to compile.\n')); console.log(chalk.red('Failed to compile.\n'));
printBuildError(err); printBuildError(err);
process.exit(1); 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. // Create the production build and print the deployment instructions.
function build(previousFileSizes) { function build(previousFileSizes) {
@@ -91,10 +127,20 @@ function build(previousFileSizes) {
let compiler = webpack(config); let compiler = webpack(config);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
compiler.run((err, stats) => { compiler.run((err, stats) => {
let messages;
if (err) { if (err) {
return reject(err); if (!err.message) {
return reject(err);
}
messages = formatWebpackMessages({
errors: [err.message],
warnings: [],
});
} else {
messages = formatWebpackMessages(
stats.toJson({ all: false, warnings: true, errors: true })
);
} }
const messages = formatWebpackMessages(stats.toJson({}, true));
if (messages.errors.length) { if (messages.errors.length) {
// Only keep the first error. Others are often indicative // Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise. // of the same problem, but confuse the reader with noise.
@@ -117,11 +163,27 @@ function build(previousFileSizes) {
); );
return reject(new Error(messages.warnings.join('\n\n'))); return reject(new Error(messages.warnings.join('\n\n')));
} }
return resolve({
const resolveArgs = {
stats, stats,
previousFileSizes, previousFileSizes,
warnings: messages.warnings, warnings: messages.warnings,
}); };
if (writeStatsJson) {
return bfj
.write(paths.appBuild + '/bundle-stats.json', stats.toJson())
.then(() => resolve(resolveArgs))
.catch(error => reject(new Error(error)));
}
return resolve(resolveArgs);
}); });
}); });
} }
function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appBuild, {
dereference: true,
filter: file => file !== paths.appHtml,
});
}
+189
View File
@@ -0,0 +1,189 @@
'use strict';
// 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('chalk');
const fs = require('fs-extra');
const webpack = require('webpack');
const bfj = require('bfj');
const config = require('../config/webpack.config.unminified');
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);
}
// Process CLI arguments
const argv = process.argv.slice(2);
const writeStatsJson = argv.indexOf('--stats') !== -1;
// 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.publicUrl;
const publicPath = config.output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
appPackage,
publicUrl,
publicPath,
buildFolder,
useYarn
);
},
err => {
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) {
console.log('Creating a non-minified production build...');
let compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
let messages;
if (err) {
if (!err.message) {
return reject(err);
}
messages = formatWebpackMessages({
errors: [err.message],
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')));
}
const resolveArgs = {
stats,
previousFileSizes,
warnings: messages.warnings,
};
if (writeStatsJson) {
return bfj
.write(paths.appBuild + '/bundle-stats.json', stats.toJson())
.then(() => resolve(resolveArgs))
.catch(error => reject(new Error(error)));
}
return resolve(resolveArgs);
});
});
}
function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appBuild, {
dereference: true,
filter: file => file !== paths.appHtml,
});
}
+75 -13
View File
@@ -14,37 +14,55 @@ process.on('unhandledRejection', err => {
// Ensure environment variables are read. // Ensure environment variables are read.
require('../config/env'); require('../config/env');
const path = require('path');
const chalk = require('chalk'); const chalk = require('chalk');
const fs = require('fs-extra'); const fs = require('fs-extra');
const webpack = require('webpack'); const webpack = require('webpack');
const bfj = require('bfj');
const config = require('../config/webpack.config.demo'); const config = require('../config/webpack.config.demo');
const paths = require('../config/paths'); const paths = require('../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter'); const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const printBuildError = require('react-dev-utils/printBuildError'); const printBuildError = require('react-dev-utils/printBuildError');
const measureFileSizesBeforeBuild = const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild; FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);
// These sizes are pretty large. We'll warn for bundles exceeding them. // These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024; const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024; const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing // Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appDemoIndexJs])) { // CRL: Updated with library index file if (!checkRequiredFiles([paths.appHtml, paths.appDemoIndexJs])) {
process.exit(1); process.exit(1);
} }
// First, read the current file sizes in build directory. // Process CLI arguments
// This lets us display how much they changed later. const argv = process.argv.slice(2);
measureFileSizesBeforeBuild(paths.appDemoBuild) const writeStatsJson = argv.indexOf('--stats') !== -1;
// 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 => { .then(previousFileSizes => {
// Remove all content but keep the directory so that // Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash // if you're in it, you don't end up in Trash
fs.emptyDirSync(paths.appDemoBuild); fs.emptyDirSync(paths.appDemo);
// Merge with the public folder
copyPublicFolder();
// Start the webpack build // Start the webpack build
return build(previousFileSizes); return build(previousFileSizes);
}) })
@@ -71,30 +89,58 @@ measureFileSizesBeforeBuild(paths.appDemoBuild)
printFileSizesAfterBuild( printFileSizesAfterBuild(
stats, stats,
previousFileSizes, previousFileSizes,
paths.appDemoBuild, paths.appDemo,
WARN_AFTER_BUNDLE_GZIP_SIZE, WARN_AFTER_BUNDLE_GZIP_SIZE,
WARN_AFTER_CHUNK_GZIP_SIZE WARN_AFTER_CHUNK_GZIP_SIZE
); );
console.log(); console.log();
const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrl;
const publicPath = config.output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appDemo);
printHostingInstructions(
appPackage,
publicUrl,
publicPath,
buildFolder,
useYarn
);
}, },
err => { err => {
console.log(chalk.red('Failed to compile.\n')); console.log(chalk.red('Failed to compile.\n'));
printBuildError(err); printBuildError(err);
process.exit(1); 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. // Create the production build and print the deployment instructions.
function build(previousFileSizes) { function build(previousFileSizes) {
console.log('Creating a build of the demo app...'); console.log('Creating an optimized production build...');
let compiler = webpack(config); let compiler = webpack(config);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
compiler.run((err, stats) => { compiler.run((err, stats) => {
let messages;
if (err) { if (err) {
return reject(err); if (!err.message) {
return reject(err);
}
messages = formatWebpackMessages({
errors: [err.message],
warnings: [],
});
} else {
messages = formatWebpackMessages(
stats.toJson({ all: false, warnings: true, errors: true })
);
} }
const messages = formatWebpackMessages(stats.toJson({}, true));
if (messages.errors.length) { if (messages.errors.length) {
// Only keep the first error. Others are often indicative // Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise. // of the same problem, but confuse the reader with noise.
@@ -117,11 +163,27 @@ function build(previousFileSizes) {
); );
return reject(new Error(messages.warnings.join('\n\n'))); return reject(new Error(messages.warnings.join('\n\n')));
} }
return resolve({
const resolveArgs = {
stats, stats,
previousFileSizes, previousFileSizes,
warnings: messages.warnings, warnings: messages.warnings,
}); };
if (writeStatsJson) {
return bfj
.write(paths.appDemo + '/bundle-stats.json', stats.toJson())
.then(() => resolve(resolveArgs))
.catch(error => reject(new Error(error)));
}
return resolve(resolveArgs);
}); });
}); });
} }
function copyPublicFolder() {
fs.copySync(paths.appPublic, paths.appDemo, {
dereference: true,
filter: file => file !== paths.appHtml,
});
}
+29 -5
View File
@@ -14,6 +14,7 @@ process.on('unhandledRejection', err => {
// Ensure environment variables are read. // Ensure environment variables are read.
require('../config/env'); require('../config/env');
const fs = require('fs'); const fs = require('fs');
const chalk = require('chalk'); const chalk = require('chalk');
const webpack = require('webpack'); const webpack = require('webpack');
@@ -35,7 +36,7 @@ const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY; const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing // Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { if (!checkRequiredFiles([paths.appHtml, paths.appDemoIndexJs])) {
process.exit(1); process.exit(1);
} }
@@ -43,9 +44,32 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000; const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
const HOST = process.env.HOST || '0.0.0.0'; const HOST = process.env.HOST || '0.0.0.0';
// We attempt to use the default port but if it is busy, we offer the user to if (process.env.HOST) {
// run on a different port. `detect()` Promise resolves to the next free port. console.log(
choosePort(HOST, DEFAULT_PORT) 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('http://bit.ly/CRA-advanced-config')}`
);
console.log();
}
// We require that you explictly 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 => { .then(port => {
if (port == null) { if (port == null) {
// We have not found a port. // We have not found a port.
@@ -59,7 +83,7 @@ choosePort(HOST, DEFAULT_PORT)
// Load proxy config // Load proxy config
const proxySetting = require(paths.appPackageJson).proxy; const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic); const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
// Serve webpack assets generated by the compiler over a web sever. // Serve webpack assets generated by the compiler over a web server.
const serverConfig = createDevServerConfig( const serverConfig = createDevServerConfig(
proxyConfig, proxyConfig,
urls.lanUrlForConfig urls.lanUrlForConfig
+31 -5
View File
@@ -15,12 +15,38 @@ process.on('unhandledRejection', err => {
// Ensure environment variables are read. // Ensure environment variables are read.
require('../config/env'); require('../config/env');
const jest = require('jest');
const argv = process.argv.slice(2);
// Watch unless on CI or in coverage mode const jest = require('jest');
if (!process.env.CI && argv.indexOf('--coverage') < 0) { const execSync = require('child_process').execSync;
argv.push('--watch'); 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, in coverage mode, or explicitly running all tests
if (
!process.env.CI &&
argv.indexOf('--coverage') === -1 &&
argv.indexOf('--watchAll') === -1
) {
// https://github.com/facebook/create-react-app/issues/5210
const hasSourceControl = isInGitRepository() || isInMercurialRepository();
argv.push(hasSourceControl ? '--watch' : '--watchAll');
} }
+2 -2
View File
@@ -31,7 +31,7 @@ class App {
onChange: input => this.onChange(input), onChange: input => this.onChange(input),
onKeyPress: button => this.onKeyPress(button), onKeyPress: button => this.onKeyPress(button),
newLineOnEnter: true, newLineOnEnter: true,
physicalKeyboardHighlight: true, physicalKeyboardHighlight: true
}); });
/** /**
@@ -43,7 +43,7 @@ class App {
</div> </div>
`); `);
document.querySelector('.input').addEventListener('change', (event) => { document.querySelector('.input').addEventListener('input', (event) => {
this.keyboard.setInput(event.target.value); this.keyboard.setInput(event.target.value);
}); });
} }
+1 -1
View File
@@ -8,7 +8,7 @@
#root .simple-keyboard-preview { #root .simple-keyboard-preview {
background: rgba(0,0,0,0.8); background: rgba(0,0,0,0.8);
border: 20px solid rgba(0,0,0,0.1); border: 20px solid rgba(0,0,0,0.1);
height: 260px; height: 200px;
border-top-right-radius: 5px; border-top-right-radius: 5px;
border-top-left-radius: 5px; border-top-left-radius: 5px;
padding: 10px; padding: 10px;
+1 -1
View File
@@ -48,7 +48,7 @@ it('Demo input change will work', () => {
demo.onDOMLoaded(); demo.onDOMLoaded();
document.body.querySelector('.input').value = "test"; document.body.querySelector('.input').value = "test";
document.body.querySelector('.input').dispatchEvent(new Event('change')); document.body.querySelector('.input').dispatchEvent(new Event('input'));
expect(demo.keyboard.getInput()).toBe("test"); expect(demo.keyboard.getInput()).toBe("test");
}); });
+219
View File
@@ -0,0 +1,219 @@
declare module 'simple-keyboard' {
interface KeyboardLayoutObject {
default: string[];
shift?: string[];
}
interface KeyboardButtonTheme {
class: string;
buttons: string;
}
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[];
/**
* Runs a `console.log` every time a key is pressed. Displays the buttons pressed and the current input.
*/
debug?: boolean;
/**
* Specifies whether clicking the "ENTER" button will input a newline (`\n`) or not.
*/
newLineOnEnter?: boolean;
/**
* Specifies whether clicking the "TAB" button will input a tab character (`\t`) or not.
*/
tabCharOnTab?: boolean;
/**
* Allows you to use a single simple-keyboard instance for several inputs.
*/
inputName?: string;
/**
* `number`: Restrains all of simple-keyboard inputs to a certain length. This should be used in addition to the input elements maxlengthattribute.
*
* `{ [inputName: string]: number }`: Restrains simple-keyboards individual inputs to a certain length. This should be used in addition to the input elements maxlengthattribute.
*/
maxLength?:
| number
| {
[inputName: string]: number;
};
/**
* 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;
/**
* Calling preventDefault for the mousedown events keeps the focus on the input.
*/
preventMouseDownDefault?: 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;
/**
* 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;
/**
* Executes the callback function on key press. Returns button layout name (i.e.: "{shift}").
*/
onKeyPress?: (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;
}
class Keyboard {
constructor(selector: string, options: KeyboardOptions);
constructor(options: KeyboardOptions);
options: KeyboardOptions;
/**
* Adds/Modifies an entry to the `buttonTheme`. Basically a way to add a class to a button.
* @param {string} buttons List of buttons to select (separated by a space).
* @param {string} className Classes to give to the selected buttons (separated by space).
*/
addButtonTheme(buttons: string, className: string): void;
/**
* Removes/Amends an entry to the `buttonTheme`. Basically a way to remove a class previously added to a button through buttonTheme or addButtonTheme.
* @param {string} buttons List of buttons to select (separated by a space).
* @param {string} className Classes to give to the selected buttons (separated by space).
*/
removeButtonTheme(buttons: string, className: string): void;
/**
* Clear the keyboard's input.
*
* @param {string} [inputName] optional - the internal input to select
*/
clearInput(inputName?: string): void;
/**
* Get the keyboards input (You can also get it from the onChange prop).
* @param {string} [inputName] optional - the internal input to select
*/
getInput(inputName?: string): string;
/**
* Set the keyboards input.
* @param {string} input the input value
* @param {string} inputName optional - the internal input to select
*/
setInput(input: string, inputName?: string): void;
/**
* 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[];
}
export default Keyboard;
}
+20 -11
View File
@@ -1,10 +1,12 @@
body, html { body,
html {
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
.simple-keyboard { .simple-keyboard {
font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue",
Helvetica, Arial, "Lucida Grande", sans-serif;
width: 100%; width: 100%;
user-select: none; user-select: none;
box-sizing: border-box; box-sizing: border-box;
@@ -30,21 +32,21 @@ body, html {
cursor: pointer; cursor: pointer;
} }
.hg-standardBtn { .simple-keyboard .hg-button span {
max-width: 100px; pointer-events: none;
} }
/** /**
* hg-theme-default theme * hg-theme-default theme
*/ */
.simple-keyboard.hg-theme-default { .simple-keyboard.hg-theme-default {
background-color: rgba(0,0,0,0.1); background-color: rgba(0, 0, 0, 0.1);
padding: 5px; padding: 5px;
border-radius: 5px; border-radius: 5px;
} }
.simple-keyboard.hg-theme-default .hg-button { .simple-keyboard.hg-theme-default .hg-button {
box-shadow: 0px 0px 3px -1px rgba(0,0,0,0.3); box-shadow: 0px 0px 3px -1px rgba(0, 0, 0, 0.3);
height: 40px; height: 40px;
border-radius: 5px; border-radius: 5px;
box-sizing: border-box; box-sizing: border-box;
@@ -55,11 +57,18 @@ body, html {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
.simple-keyboard.hg-theme-default .hg-button:active { /* When using option "useButtonTag" */
.simple-keyboard button.hg-button {
border-width: 0;
outline: 0;
font-size: inherit;
}
.simple-keyboard.hg-theme-default:not(.hg-touch-events) .hg-button:active {
background: #e4e4e4; background: #e4e4e4;
} }
.simple-keyboard.hg-theme-default.hg-layout-numeric .hg-button { .simple-keyboard.hg-theme-default.hg-layout-numeric .hg-button {
width: 33.3%; width: 33.3%;
@@ -100,4 +109,4 @@ body, html {
.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn[data-skbtn="@"] { .simple-keyboard.hg-theme-default .hg-button.hg-standardBtn[data-skbtn="@"] {
max-width: 60px; max-width: 60px;
} }
File diff suppressed because it is too large Load Diff
+197 -16
View File
@@ -45,6 +45,32 @@ it('Keyboard will run with debug option set', () => {
expect(keyboard.options.debug).toBeTruthy(); expect(keyboard.options.debug).toBeTruthy();
}); });
it('Keyboard will use touch events', () => {
let touched = false
testUtil.clear()
document.body.innerHTML = `
<div id="keyboard"></div>
`;
const keyboard = new Keyboard('#keyboard', {
useTouchEvents: true,
onChange: () => touched = true,
layout: {
default: ["q"]
}
});
keyboard.getButtonElement("q").ontouchstart();
keyboard.getButtonElement("q").ontouchend();
keyboard.getButtonElement("q").ontouchcancel();
expect(keyboard.options.useTouchEvents).toBeTruthy();
expect(touched).toBeTruthy();
expect(keyboard.getInput()).toBe('q');
})
it('Keyboard standard buttons will work', () => { it('Keyboard standard buttons will work', () => {
testUtil.setDOM(); testUtil.setDOM();
let keyboard = new Keyboard({ let keyboard = new Keyboard({
@@ -111,7 +137,9 @@ it('Keyboard onKeyPress will work', () => {
it('Keyboard standard function buttons will not change input', () => { it('Keyboard standard function buttons will not change input', () => {
testUtil.setDOM(); testUtil.setDOM();
let keyboard = new Keyboard(); let keyboard = new Keyboard({
useButtonTag: true
});
testUtil.iterateButtons((button) => { testUtil.iterateButtons((button) => {
if(button.getAttribute("data-skbtn") === "{shift}"){ if(button.getAttribute("data-skbtn") === "{shift}"){
@@ -158,20 +186,6 @@ it('Keyboard onChange will work', () => {
expect(output).toBe("q"); expect(output).toBe("q");
}); });
it('Keyboard clearInput will work', () => {
testUtil.setDOM();
let keyboard = new Keyboard();
keyboard.input = {
"default": "hello"
};
keyboard.clearInput();
expect(keyboard.getInput()).toBeFalsy();
});
it('Keyboard clearInput will work', () => { it('Keyboard clearInput will work', () => {
testUtil.setDOM(); testUtil.setDOM();
@@ -570,11 +584,49 @@ it('Keyboard caretEventHandler will detect input, textarea focus', () => {
expect(keyboard.caretPosition).toBe(3); expect(keyboard.caretPosition).toBe(3);
}); });
it('Keyboard caretEventHandler will not set caretPosition on disableCaretPositioning', () => {
testUtil.setDOM();
let keyboard = new Keyboard();
keyboard.caretEventHandler({
charCode: 0,
code: "KeyF",
key: "f",
which: 70,
target: {
tagName: "input",
selectionStart: 3
}
});
expect(keyboard.caretPosition).toBe(3);
keyboard.setOptions({
disableCaretPositioning: true
});
keyboard.caretEventHandler({
charCode: 0,
code: "KeyF",
key: "f",
which: 70,
target: {
tagName: "input",
selectionStart: 3
}
});
expect(keyboard.caretPosition).toBeFalsy();
});
it('Keyboard caretEventHandler ignore positioning if input, textarea is blur', () => { it('Keyboard caretEventHandler ignore positioning if input, textarea is blur', () => {
testUtil.setDOM(); testUtil.setDOM();
let keyboard = new Keyboard(); let keyboard = new Keyboard();
keyboard.isMouseHold = true;
keyboard.caretEventHandler({ keyboard.caretEventHandler({
charCode: 0, charCode: 0,
code: "KeyF", code: "KeyF",
@@ -835,7 +887,27 @@ it('Keyboard handleButtonMouseDown will work', () => {
target: keyboard.getButtonElement("q") target: keyboard.getButtonElement("q")
}); });
keyboard.getButtonElement("q").onmousedown(); var clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent('mousedown', true, true);
keyboard.getButtonElement("q").dispatchEvent(clickEvent);
document.onmouseup();
});
it('Keyboard handleButtonMouseDown will work with preventMouseDownDefault', () => {
testUtil.setDOM();
let keyboard = new Keyboard();
keyboard.options.preventMouseDownDefault = true;
keyboard.handleButtonMouseDown("q", {
target: keyboard.getButtonElement("q")
});
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent('mousedown', true, true);
keyboard.getButtonElement("q").dispatchEvent(clickEvent);
document.onmouseup(); document.onmouseup();
}); });
@@ -861,4 +933,113 @@ it('Keyboard onModulesLoaded will work', () => {
}); });
expect(foo).toBe("bar"); expect(foo).toBe("bar");
});
it('Keyboard inputPattern will work globally', () => {
testUtil.setDOM();
let keyboard = new Keyboard({
inputPattern: /^\d+$/
});
keyboard.getButtonElement("q").onclick();
expect(keyboard.getInput()).toBeFalsy();
keyboard.getButtonElement("1").onclick();
expect(keyboard.getInput()).toBe("1");
});
it('Keyboard inputPattern will work by input name', () => {
testUtil.setDOM();
let keyboard = new Keyboard({
debug: true,
inputName: "test1",
inputPattern: {
test1: /^\d+$/
}
});
keyboard.getButtonElement("q").onclick();
keyboard.getButtonElement("1").onclick();
expect(keyboard.getInput()).toBe("1");
keyboard.setOptions({
inputName: "default"
});
keyboard.getButtonElement("q").onclick();
keyboard.getButtonElement("1").onclick();
expect(keyboard.getInput()).toBe("q1");
});
it('Keyboard processAutoTouchEvents will work', () => {
testUtil.setDOM();
navigator.maxTouchPoints = true;
let keyboard = new Keyboard({
autoUseTouchEvents: true
});
expect(keyboard.options.useTouchEvents).toBeTruthy();
});
it('Keyboard processAutoTouchEvents will work with debugging enabled', () => {
testUtil.setDOM();
navigator.maxTouchPoints = true;
let keyboard = new Keyboard({
autoUseTouchEvents: true,
debug: true
});
expect(keyboard.options.useTouchEvents).toBeTruthy();
});
it('Keyboard beforeFirstRender method will work', () => {
testUtil.setDOM();
let timesCalled = 0;
let keyboard = new Keyboard({
beforeFirstRender: () => {
timesCalled++;
}
});
/**
* Triggering another render
*/
keyboard.setOptions({
layoutName: "shift"
});
expect(timesCalled).toBe(1);
});
it('Keyboard beforeRender method will work', () => {
testUtil.setDOM();
let timesCalled = 0;
let keyboard = new Keyboard({
beforeRender: () => {
timesCalled++;
}
});
/**
* Triggering another render
*/
keyboard.setOptions({
layoutName: "shift"
});
expect(timesCalled).toBe(2);
}); });
+1 -1
View File
@@ -1,2 +1,2 @@
import SimpleKeyboard from './components/Keyboard'; import SimpleKeyboard from "./components/Keyboard";
export default SimpleKeyboard; export default SimpleKeyboard;
+14 -14
View File
@@ -6,24 +6,24 @@ class KeyboardLayout {
* Get default simple-keyboard layout * Get default simple-keyboard layout
* @return {object} The default layout (US-QWERTY) * @return {object} The default layout (US-QWERTY)
*/ */
static getDefaultLayout(){ static getDefaultLayout() {
return { return {
'default': [ default: [
'` 1 2 3 4 5 6 7 8 9 0 - = {bksp}', "` 1 2 3 4 5 6 7 8 9 0 - = {bksp}",
'{tab} q w e r t y u i o p [ ] \\', "{tab} q w e r t y u i o p [ ] \\",
'{lock} a s d f g h j k l ; \' {enter}', "{lock} a s d f g h j k l ; ' {enter}",
'{shift} z x c v b n m , . / {shift}', "{shift} z x c v b n m , . / {shift}",
'.com @ {space}' ".com @ {space}"
], ],
'shift': [ shift: [
'~ ! @ # $ % ^ & * ( ) _ + {bksp}', "~ ! @ # $ % ^ & * ( ) _ + {bksp}",
'{tab} Q W E R T Y U I O P { } |', "{tab} Q W E R T Y U I O P { } |",
'{lock} A S D F G H J K L : " {enter}', '{lock} A S D F G H J K L : " {enter}',
'{shift} Z X C V B N M < > ? {shift}', "{shift} Z X C V B N M < > ? {shift}",
'.com @ {space}' ".com @ {space}"
] ]
} };
} }
} }
export default KeyboardLayout; export default KeyboardLayout;
+30 -18
View File
@@ -5,7 +5,7 @@ class PhysicalKeyboard {
/** /**
* Creates an instance of the PhysicalKeyboard service * Creates an instance of the PhysicalKeyboard service
*/ */
constructor(simpleKeyboardInstance){ constructor(simpleKeyboardInstance) {
/** /**
* @type {object} A simple-keyboard instance * @type {object} A simple-keyboard instance
*/ */
@@ -15,7 +15,9 @@ class PhysicalKeyboard {
* Bindings * Bindings
*/ */
this.initKeyboardListener = this.initKeyboardListener.bind(this); this.initKeyboardListener = this.initKeyboardListener.bind(this);
this.getSimpleKeyboardLayoutKey = this.getSimpleKeyboardLayoutKey.bind(this); this.getSimpleKeyboardLayoutKey = this.getSimpleKeyboardLayoutKey.bind(
this
);
/** /**
* Initialize key listeners * Initialize key listeners
@@ -26,32 +28,40 @@ class PhysicalKeyboard {
/** /**
* Initializes key event listeners * Initializes key event listeners
*/ */
initKeyboardListener(){ initKeyboardListener() {
// Adding button style on keydown // Adding button style on keydown
document.addEventListener("keydown", (event) => { document.addEventListener("keydown", event => {
if(this.simpleKeyboardInstance.options.physicalKeyboardHighlight){ if (this.simpleKeyboardInstance.options.physicalKeyboardHighlight) {
let buttonPressed = this.getSimpleKeyboardLayoutKey(event); let buttonPressed = this.getSimpleKeyboardLayoutKey(event);
this.simpleKeyboardInstance.dispatch(instance => { this.simpleKeyboardInstance.dispatch(instance => {
let buttonDOM = instance.getButtonElement(buttonPressed) || instance.getButtonElement(`{${buttonPressed}}`); let buttonDOM =
instance.getButtonElement(buttonPressed) ||
instance.getButtonElement(`{${buttonPressed}}`);
if(buttonDOM){ if (buttonDOM) {
buttonDOM.style.backgroundColor = this.simpleKeyboardInstance.options.physicalKeyboardHighlightBgColor || "#9ab4d0"; buttonDOM.style.backgroundColor =
buttonDOM.style.color = this.simpleKeyboardInstance.options.physicalKeyboardHighlightTextColor || "white"; this.simpleKeyboardInstance.options
.physicalKeyboardHighlightBgColor || "#9ab4d0";
buttonDOM.style.color =
this.simpleKeyboardInstance.options
.physicalKeyboardHighlightTextColor || "white";
} }
}); });
} }
}); });
// Removing button style on keyup // Removing button style on keyup
document.addEventListener("keyup", (event) => { document.addEventListener("keyup", event => {
if(this.simpleKeyboardInstance.options.physicalKeyboardHighlight){ if (this.simpleKeyboardInstance.options.physicalKeyboardHighlight) {
let buttonPressed = this.getSimpleKeyboardLayoutKey(event); let buttonPressed = this.getSimpleKeyboardLayoutKey(event);
this.simpleKeyboardInstance.dispatch(instance => { this.simpleKeyboardInstance.dispatch(instance => {
let buttonDOM = instance.getButtonElement(buttonPressed) || instance.getButtonElement(`{${buttonPressed}}`); let buttonDOM =
instance.getButtonElement(buttonPressed) ||
instance.getButtonElement(`{${buttonPressed}}`);
if(buttonDOM){ if (buttonDOM && buttonDOM.removeAttribute) {
buttonDOM.removeAttribute("style"); buttonDOM.removeAttribute("style");
} }
}); });
@@ -63,10 +73,10 @@ class PhysicalKeyboard {
* Transforms a KeyboardEvent's "key.code" string into a simple-keyboard layout format * Transforms a KeyboardEvent's "key.code" string into a simple-keyboard layout format
* @param {object} event The KeyboardEvent * @param {object} event The KeyboardEvent
*/ */
getSimpleKeyboardLayoutKey(event){ getSimpleKeyboardLayoutKey(event) {
let output; let output;
if( if (
event.code.includes("Numpad") || event.code.includes("Numpad") ||
event.code.includes("Shift") || event.code.includes("Shift") ||
event.code.includes("Space") || event.code.includes("Space") ||
@@ -74,7 +84,7 @@ class PhysicalKeyboard {
event.code.includes("Control") || event.code.includes("Control") ||
event.code.includes("Alt") || event.code.includes("Alt") ||
event.code.includes("Meta") event.code.includes("Meta")
){ ) {
output = event.code; output = event.code;
} else { } else {
output = event.key; output = event.key;
@@ -85,7 +95,9 @@ class PhysicalKeyboard {
*/ */
if ( if (
output !== output.toUpperCase() || output !== output.toUpperCase() ||
(event.code[0] === "F" && Number.isInteger(Number(event.code[1])) && event.code.length <= 3) (event.code[0] === "F" &&
Number.isInteger(Number(event.code[1])) &&
event.code.length <= 3)
) { ) {
output = output.toLowerCase(); output = output.toLowerCase();
} }
@@ -94,4 +106,4 @@ class PhysicalKeyboard {
} }
} }
export default PhysicalKeyboard; export default PhysicalKeyboard;
+185 -123
View File
@@ -5,7 +5,7 @@ class Utilities {
/** /**
* Creates an instance of the Utility service * Creates an instance of the Utility service
*/ */
constructor(simpleKeyboardInstance){ constructor(simpleKeyboardInstance) {
/** /**
* @type {object} A simple-keyboard instance * @type {object} A simple-keyboard instance
*/ */
@@ -14,27 +14,24 @@ class Utilities {
/** /**
* Bindings * Bindings
*/ */
this.getButtonClass = this.getButtonClass.bind(this); Utilities.bindMethods(Utilities, this);
this.getButtonDisplayName = this.getButtonDisplayName.bind(this);
this.getUpdatedInput = this.getUpdatedInput.bind(this);
this.updateCaretPos = this.updateCaretPos.bind(this);
this.isMaxLengthReached = this.isMaxLengthReached.bind(this);
this.camelCase = this.camelCase.bind(this);
this.countInArray = this.countInArray.bind(this);
} }
/** /**
* Adds default classes to a given button * Adds default classes to a given button
* *
* @param {string} button The button's layout name * @param {string} button The button's layout name
* @return {string} The classes to be added to the button * @return {string} The classes to be added to the button
*/ */
getButtonClass(button){ getButtonClass(button) {
let buttonTypeClass = (button.includes("{") && button.includes("}") && button !== '{//}') ? "functionBtn" : "standardBtn"; let buttonTypeClass =
button.includes("{") && button.includes("}") && button !== "{//}"
? "functionBtn"
: "standardBtn";
let buttonWithoutBraces = button.replace("{", "").replace("}", ""); let buttonWithoutBraces = button.replace("{", "").replace("}", "");
let buttonNormalized = ''; let buttonNormalized = "";
if(buttonTypeClass !== "standardBtn") if (buttonTypeClass !== "standardBtn")
buttonNormalized = ` hg-button-${buttonWithoutBraces}`; buttonNormalized = ` hg-button-${buttonWithoutBraces}`;
return `hg-${buttonTypeClass}${buttonNormalized}`; return `hg-${buttonTypeClass}${buttonNormalized}`;
@@ -43,22 +40,22 @@ class Utilities {
/** /**
* Default button display labels * Default button display labels
*/ */
getDefaultDiplay(){ getDefaultDiplay() {
return { return {
'{bksp}': 'backspace', "{bksp}": "backspace",
'{backspace}': 'backspace', "{backspace}": "backspace",
'{enter}': '< enter', "{enter}": "< enter",
'{shift}': 'shift', "{shift}": "shift",
'{shiftleft}': 'shift', "{shiftleft}": "shift",
'{shiftright}': 'shift', "{shiftright}": "shift",
'{alt}': 'alt', "{alt}": "alt",
'{s}': 'shift', "{s}": "shift",
'{tab}': 'tab', "{tab}": "tab",
'{lock}': 'caps', "{lock}": "caps",
'{capslock}': 'caps', "{capslock}": "caps",
'{accept}': 'Submit', "{accept}": "Submit",
'{space}': ' ', "{space}": " ",
'{//}': ' ', "{//}": " ",
"{esc}": "esc", "{esc}": "esc",
"{escape}": "esc", "{escape}": "esc",
"{f1}": "f1", "{f1}": "f1",
@@ -73,8 +70,8 @@ class Utilities {
"{f10}": "f10", "{f10}": "f10",
"{f11}": "f11", "{f11}": "f11",
"{f12}": "f12", "{f12}": "f12",
'{numpaddivide}': '/', "{numpaddivide}": "/",
'{numlock}': 'lock', "{numlock}": "lock",
"{arrowup}": "↑", "{arrowup}": "↑",
"{arrowleft}": "←", "{arrowleft}": "←",
"{arrowdown}": "↓", "{arrowdown}": "↓",
@@ -103,18 +100,18 @@ class Utilities {
"{numpad6}": "6", "{numpad6}": "6",
"{numpad7}": "7", "{numpad7}": "7",
"{numpad8}": "8", "{numpad8}": "8",
"{numpad9}": "9", "{numpad9}": "9"
}; };
} }
/** /**
* Returns the display (label) name for a given button * Returns the display (label) name for a given button
* *
* @param {string} button The button's layout name * @param {string} button The button's layout name
* @param {object} display The provided display option * @param {object} display The provided display option
* @param {boolean} mergeDisplay Whether the provided param value should be merged with the default one. * @param {boolean} mergeDisplay Whether the provided param value should be merged with the default one.
*/ */
getButtonDisplayName(button, display, mergeDisplay){ getButtonDisplayName(button, display, mergeDisplay) {
if(mergeDisplay){ if (mergeDisplay) {
display = Object.assign({}, this.getDefaultDiplay(), display); display = Object.assign({}, this.getDefaultDiplay(), display);
} else { } else {
display = display || this.getDefaultDiplay(); display = display || this.getDefaultDiplay();
@@ -123,97 +120,136 @@ class Utilities {
return display[button] || button; return display[button] || button;
} }
/** /**
* Returns the updated input resulting from clicking a given button * Returns the updated input resulting from clicking a given button
* *
* @param {string} button The button's layout name * @param {string} button The button's layout name
* @param {string} input The input string * @param {string} input The input string
* @param {object} options The simple-keyboard options object * @param {object} options The simple-keyboard options object
* @param {number} caretPos The cursor's current position * @param {number} caretPos The cursor's current position
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/ */
getUpdatedInput(button, input, options, caretPos){ getUpdatedInput(button, input, options, caretPos, moveCaret) {
let output = input; let output = input;
if((button === "{bksp}" || button === "{backspace}") && output.length > 0){ if (
output = this.removeAt(output, caretPos); (button === "{bksp}" || button === "{backspace}") &&
output.length > 0
} else if(button === "{space}") ) {
output = this.addStringAt(output, " ", caretPos); output = this.removeAt(output, caretPos, moveCaret);
} else if (button === "{space}")
else if(button === "{tab}" && !(typeof options.tabCharOnTab === "boolean" && options.tabCharOnTab === false)){ output = this.addStringAt(output, " ", caretPos, moveCaret);
output = this.addStringAt(output, "\t", caretPos); else if (
button === "{tab}" &&
} else if((button === "{enter}" || button === "{numpadenter}") && options.newLineOnEnter) !(
output = this.addStringAt(output, "\n", caretPos); typeof options.tabCharOnTab === "boolean" &&
options.tabCharOnTab === false
else if(button.includes("numpad") && Number.isInteger(Number(button[button.length - 2]))){ )
output = this.addStringAt(output, button[button.length - 2], caretPos); ) {
} output = this.addStringAt(output, "\t", caretPos, moveCaret);
else if(button === "{numpaddivide}") } else if (
output = this.addStringAt(output, '/', caretPos); (button === "{enter}" || button === "{numpadenter}") &&
options.newLineOnEnter
else if(button === "{numpadmultiply}") )
output = this.addStringAt(output, '*', caretPos); output = this.addStringAt(output, "\n", caretPos, moveCaret);
else if(button === "{numpadsubtract}") else if (
output = this.addStringAt(output, '-', caretPos); button.includes("numpad") &&
Number.isInteger(Number(button[button.length - 2]))
else if(button === "{numpadadd}") ) {
output = this.addStringAt(output, '+', caretPos); output = this.addStringAt(
output,
else if(button === "{numpaddecimal}") button[button.length - 2],
output = this.addStringAt(output, '.', caretPos); caretPos,
moveCaret
else if(button === "{" || button === "}") );
output = this.addStringAt(output, button, caretPos); } else if (button === "{numpaddivide}")
output = this.addStringAt(output, "/", caretPos, moveCaret);
else if(!button.includes("{") && !button.includes("}")) else if (button === "{numpadmultiply}")
output = this.addStringAt(output, button, caretPos); output = this.addStringAt(output, "*", caretPos, moveCaret);
else if (button === "{numpadsubtract}")
output = this.addStringAt(output, "-", caretPos, moveCaret);
else if (button === "{numpadadd}")
output = this.addStringAt(output, "+", caretPos, moveCaret);
else if (button === "{numpaddecimal}")
output = this.addStringAt(output, ".", caretPos, moveCaret);
else if (button === "{" || button === "}")
output = this.addStringAt(output, button, caretPos, moveCaret);
else if (!button.includes("{") && !button.includes("}"))
output = this.addStringAt(output, button, caretPos, moveCaret);
return output; return output;
} }
/** /**
* Moves the cursor position by a given amount * Moves the cursor position by a given amount
* *
* @param {number} length Represents by how many characters the input should be moved * @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. * @param {boolean} minus Whether the cursor should be moved to the left or not.
*/ */
updateCaretPos(length, minus){ updateCaretPos(length, minus) {
if(minus){ let newCaretPos = this.updateCaretPosAction(
if(this.simpleKeyboardInstance.caretPosition > 0) this.simpleKeyboardInstance,
this.simpleKeyboardInstance.caretPosition = this.simpleKeyboardInstance.caretPosition - length length,
} else { minus
this.simpleKeyboardInstance.caretPosition = this.simpleKeyboardInstance.caretPosition + length; );
if (this.simpleKeyboardInstance.options.syncInstanceInputs) {
this.simpleKeyboardInstance.dispatch(instance => {
instance.caretPosition = newCaretPos;
});
} }
} }
/**
* Action method of updateCaretPos
*
* @param {object} instance The instance whose position should be updated
* @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(instance, length, minus) {
if (minus) {
if (instance.caretPosition > 0)
instance.caretPosition = instance.caretPosition - length;
} else {
instance.caretPosition = instance.caretPosition + length;
}
if (this.simpleKeyboardInstance.options.debug) {
console.log(
"Caret at:",
instance.caretPosition,
`(${instance.keyboardDOMClass})`
);
}
return instance.caretPosition;
}
/** /**
* Adds a string to the input at a given position * Adds a string to the input at a given position
* *
* @param {string} source The source input * @param {string} source The source input
* @param {string} string The string to add * @param {string} string The string to add
* @param {number} position The (cursor) position where the string should be added * @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, position){ addStringAt(source, string, position, moveCaret) {
let output; let output;
if(this.simpleKeyboardInstance.options.debug){ if (!position && position !== 0) {
console.log("Caret at:", position);
}
if(!position && position !== 0){
output = source + string; output = source + string;
} else { } else {
output = [source.slice(0, position), string, source.slice(position)].join(''); output = [source.slice(0, position), string, source.slice(position)].join(
""
);
/** /**
* Avoid caret position change when maxLength is set * Avoid caret position change when maxLength is set
*/ */
if(!this.isMaxLengthReached()){ if (!this.isMaxLengthReached()) {
this.updateCaretPos(string.length); if (moveCaret) this.updateCaretPos(string.length);
} }
} }
return output; return output;
@@ -221,12 +257,13 @@ class Utilities {
/** /**
* Removes an amount of characters at a given position * Removes an amount of characters at a given position
* *
* @param {string} source The source input * @param {string} source The source input
* @param {number} position The (cursor) position from where the characters should be removed * @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, position){ removeAt(source, position, moveCaret) {
if(this.simpleKeyboardInstance.caretPosition === 0){ if (this.simpleKeyboardInstance.caretPosition === 0) {
return source; return source;
} }
@@ -239,27 +276,27 @@ class Utilities {
* Emojis are made out of two characters, so we must take a custom approach to trim them. * Emojis are made out of two characters, so we must take a custom approach to trim them.
* For more info: https://mathiasbynens.be/notes/javascript-unicode * For more info: https://mathiasbynens.be/notes/javascript-unicode
*/ */
if(position && position >= 0){ if (position && position >= 0) {
prevTwoChars = source.substring(position - 2, position) prevTwoChars = source.substring(position - 2, position);
emojiMatched = prevTwoChars.match(emojiMatchedReg); emojiMatched = prevTwoChars.match(emojiMatchedReg);
if(emojiMatched){ if (emojiMatched) {
output = source.substr(0, (position - 2)) + source.substr(position); output = source.substr(0, position - 2) + source.substr(position);
this.updateCaretPos(2, true); if (moveCaret) this.updateCaretPos(2, true);
} else { } else {
output = source.substr(0, (position - 1)) + source.substr(position); output = source.substr(0, position - 1) + source.substr(position);
this.updateCaretPos(1, true); if (moveCaret) this.updateCaretPos(1, true);
} }
} else { } else {
prevTwoChars = source.slice(-2); prevTwoChars = source.slice(-2);
emojiMatched = prevTwoChars.match(emojiMatchedReg); emojiMatched = prevTwoChars.match(emojiMatchedReg);
if(emojiMatched){ if (emojiMatched) {
output = source.slice(0, -2); output = source.slice(0, -2);
this.updateCaretPos(2, true); if (moveCaret) this.updateCaretPos(2, true);
} else { } else {
output = source.slice(0, -1); output = source.slice(0, -1);
this.updateCaretPos(1, true); if (moveCaret) this.updateCaretPos(1, true);
} }
} }
@@ -267,33 +304,32 @@ class Utilities {
} }
/** /**
* Determines whether the maxLength has been reached. This function is called when the maxLength option it set. * Determines whether the maxLength has been reached. This function is called when the maxLength option it set.
* *
* @param {object} inputObj * @param {object} inputObj
* @param {object} options * @param {object} options
* @param {string} updatedInput * @param {string} updatedInput
*/ */
handleMaxLength(inputObj, options, updatedInput){ handleMaxLength(inputObj, options, updatedInput) {
let maxLength = options.maxLength; let maxLength = options.maxLength;
let currentInput = inputObj[options.inputName]; let currentInput = inputObj[options.inputName];
let condition = currentInput.length === maxLength; let condition = currentInput.length === maxLength;
if (
if(
/** /**
* If pressing this button won't add more characters * If pressing this button won't add more characters
* We exit out of this limiter function * We exit out of this limiter function
*/ */
updatedInput.length <= currentInput.length updatedInput.length <= currentInput.length
){ ) {
return false; return false;
} }
if(Number.isInteger(maxLength)){ if (Number.isInteger(maxLength)) {
if(options.debug){ if (options.debug) {
console.log("maxLength (num) reached:", condition); console.log("maxLength (num) reached:", condition);
} }
if(condition){ if (condition) {
/** /**
* @type {boolean} Boolean value that shows whether maxLength has been reached * @type {boolean} Boolean value that shows whether maxLength has been reached
*/ */
@@ -305,14 +341,14 @@ class Utilities {
} }
} }
if(typeof maxLength === "object"){ if (typeof maxLength === "object") {
let condition = currentInput.length === maxLength[options.inputName]; let condition = currentInput.length === maxLength[options.inputName];
if(options.debug){ if (options.debug) {
console.log("maxLength (obj) reached:", condition); console.log("maxLength (obj) reached:", condition);
} }
if(condition){ if (condition) {
this.maxLengthReached = true; this.maxLengthReached = true;
return true; return true;
} else { } else {
@@ -325,29 +361,55 @@ class Utilities {
/** /**
* Gets the current value of maxLengthReached * Gets the current value of maxLengthReached
*/ */
isMaxLengthReached(){ isMaxLengthReached() {
return Boolean(this.maxLengthReached); return Boolean(this.maxLengthReached);
} }
/**
* Determines whether a touch device is being used
*/
isTouchDevice() {
return "ontouchstart" in window || navigator.maxTouchPoints;
}
/**
* Bind all methods in a given class
*/
static bindMethods(myClass, instance) {
for (let myMethod of Object.getOwnPropertyNames(myClass.prototype)) {
let excludeMethod =
myMethod === "constructor" || myMethod === "bindMethods";
if (!excludeMethod) {
instance[myMethod] = instance[myMethod].bind(instance);
}
}
}
/** /**
* Transforms an arbitrary string to camelCase * Transforms an arbitrary string to camelCase
* *
* @param {string} string The string to transform. * @param {string} string The string to transform.
*/ */
camelCase(string){ camelCase(string) {
return string.toLowerCase().trim().split(/[.\-_\s]/g).reduce((string, word) => string + word[0].toUpperCase() + word.slice(1)); return string
}; .toLowerCase()
.trim()
.split(/[.\-_\s]/g)
.reduce((string, word) =>
word.length ? string + word[0].toUpperCase() + word.slice(1) : string
);
}
/** /**
* Counts the number of duplicates in a given array * Counts the number of duplicates in a given array
* *
* @param {Array} array The haystack to search in * @param {Array} array The haystack to search in
* @param {string} value The needle to search for * @param {string} value The needle to search for
*/ */
countInArray(array, value){ countInArray(array, value) {
return array.reduce((n, x) => n + (x === value), 0); return array.reduce((n, x) => n + (x === value), 0);
} }
} }
export default Utilities; export default Utilities;
+36 -6
View File
@@ -210,6 +210,18 @@ it('Keyboard standard button will affect input', () => {
} }
}); });
it('Keyboard updateCaretPos will work with minus', () => {
testUtil.setDOM();
let keyboard = new Keyboard();
keyboard.options.syncInstanceInputs = true;
keyboard.caretPosition = 5;
keyboard.utilities.updateCaretPos(2, true);
expect(keyboard.caretPosition).toBe(3);
});
it('Keyboard updateCaretPos will work with minus', () => { it('Keyboard updateCaretPos will work with minus', () => {
testUtil.setDOM(); testUtil.setDOM();
@@ -416,11 +428,14 @@ it('Keyboard removeAt will exit out on caretPosition:0', () => {
let keyboard = new Keyboard(); let keyboard = new Keyboard();
keyboard.setInput("test"); keyboard.setInput("test");
keyboard.caretPosition = 0; keyboard.caretPosition = 0;
keyboard.utilities.removeAt(keyboard.getInput(), 0); keyboard.utilities.removeAt(keyboard.getInput(), 0);
expect(keyboard.getInput()).toBe("test"); expect(keyboard.getInput()).toBe("test");
keyboard.setInput("test");
keyboard.caretPosition = 5;
keyboard.utilities.removeAt(keyboard.getInput(), 0, true);
expect(keyboard.caretPosition).toBe(4);
}); });
it('Keyboard removeAt will remove multi-byte unicodes with caretPos>0', () => { it('Keyboard removeAt will remove multi-byte unicodes with caretPos>0', () => {
@@ -430,27 +445,42 @@ it('Keyboard removeAt will remove multi-byte unicodes with caretPos>0', () => {
keyboard.caretPosition = 6; keyboard.caretPosition = 6;
let output = keyboard.utilities.removeAt("test\uD83D\uDE00", 6); let output = keyboard.utilities.removeAt("test\uD83D\uDE00", 6);
expect(output).toBe("test"); expect(output).toBe("test");
keyboard.caretPosition = 6;
output = keyboard.utilities.removeAt("test\uD83D\uDE00", 6, true);
expect(keyboard.caretPosition).toBe(4);
}); });
it('Keyboard removeAt will not remove multi-byte unicodes with caretPos:0', () => { it('Keyboard removeAt will not remove multi-byte unicodes with caretPos:0', () => {
testUtil.setDOM(); testUtil.setDOM();
let keyboard = new Keyboard(); let keyboard = new Keyboard();
let output = keyboard.utilities.removeAt("\uD83D\uDE00"); let output = keyboard.utilities.removeAt("\uD83D\uDE00");
expect(output).toBeFalsy();
output = keyboard.utilities.removeAt("\uD83D\uDE00", 0, true);
expect(output).toBeFalsy(); expect(output).toBeFalsy();
}); });
it('Keyboard removeAt will remove regular strings', () => { it('Keyboard removeAt will remove regular strings', () => {
testUtil.setDOM(); testUtil.setDOM();
let keyboard = new Keyboard(); let keyboard = new Keyboard({
debug: true
});
keyboard.caretPosition = 6; keyboard.caretPosition = 6;
let output = keyboard.utilities.removeAt("testie", 6); let output = keyboard.utilities.removeAt("testie", 6);
expect(output).toBe("testi"); expect(output).toBe("testi");
keyboard.caretPosition = 6;
output = keyboard.utilities.removeAt("testie", 6, true);
expect(keyboard.caretPosition).toBe(5);
});
it('Keyboard will work with custom (and weird) class', () => {
testUtil.setDOM("my--weird--class");
let keyboard = new Keyboard(".my--weird--class");
expect(keyboard.keyboardDOMClass).toBe("my--weird--class");
}); });