Compare commits

..

163 Commits

Author SHA1 Message Date
Francisco Hodge
652084cabb npm update 2019-03-09 21:47:49 -05:00
Francisco Hodge
39c4acba85 Tests update 2019-03-09 21:47:39 -05:00
Francisco Hodge
69b830c632 Added row button grouping functionality 2019-03-09 21:47:31 -05:00
Francisco Hodge
cd909cffa8 Build update 2019-03-07 21:30:17 -05:00
Francisco Hodge
92d2b381db npm update 2019-03-07 21:26:43 -05:00
Francisco Hodge
96206d1b47 Remove PointerEvents debug message when useMouseEvents is enabled 2019-03-07 21:26:22 -05:00
Francisco Hodge
19f6ac9ac1 Build update 2019-03-07 20:53:23 -05:00
Francisco Hodge
90b67ec416 Support Utilities access through Typescript 2019-03-07 20:53:07 -05:00
Francisco Hodge
6208d5f7e1 Fixed getInput output when inputName is passed 2019-03-07 20:52:40 -05:00
Francisco Hodge
bd0274415b Fixed onChangeAll callback 2019-03-07 20:51:57 -05:00
Francisco Hodge
30941f4ba0 Adding useMouseEvents option 2019-03-07 20:51:29 -05:00
Francisco Hodge
48a15e6715 Disable devDependency tracking 2019-03-07 20:49:34 -05:00
Francisco Hodge
f8ee859bb0 Build update 2019-03-07 07:36:08 -05:00
Francisco Hodge
3012ce24e2 Build update 2019-03-07 07:32:24 -05:00
Francisco Hodge
6e54729f46 Tests update 2019-03-07 07:32:15 -05:00
Francisco Hodge
5ab58f2528 Using PointerEvents on browsers that support it 2019-03-07 07:32:07 -05:00
Francisco Hodge
47ddf0b1e8 Build update 2019-03-06 20:26:46 -05:00
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 Hodge
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 Hodge
f5b73b09e7 Merge pull request #45 from mkusher/patch-1
fixed camelCase function
2018-12-27 13:33:09 -05:00
Aleh Kashnikau
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 Hodge
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 Hodge
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 Hodge
178c63cf98 Update README.md 2018-11-27 17:49:12 -05:00
Francisco Hodge
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 Hodge
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 Hodge
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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]
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 119841 additions and 88990 deletions

View File

@@ -1,17 +1,17 @@
---
name: Bug report
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.
**Describe the bug**
A clear and concise description of what the bug is.
**Create a sandbox example**
Edit [this sandbox example](https://codesandbox.io/s/krzkx19rr) and provide the new link.
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.
**Screenshots**
If applicable, add screenshots to help explain your problem.

View File

@@ -1,6 +1,9 @@
---
name: Feature request
about: Suggest an idea you'd like to see in simple-keyboard
title: ''
labels: ''
assignees: ''
---

8
.github/pull_request_template.md vendored Normal file
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%

4
.gitignore vendored
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
/node_modules
/.pnp
.pnp.js
# production
/demo

View File

@@ -1,11 +1,12 @@
language: node_js
node_js:
- "node"
- '11.10.1'
install:
- npm install -g codecov
- npm install
script:
- npm run test -- --coverage
- npm run demo
- codecov
after_success:
- wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh

View File

@@ -1,6 +1,6 @@
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
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
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

View File

@@ -1,12 +1,12 @@
<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/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>
<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://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://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://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>
</div>
@@ -34,7 +34,9 @@ Feel free to browse the [Q&A / Use-cases](https://simple-keyboard.com/qa-use-cas
### 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?

View File

@@ -1,6 +1,6 @@
/*!
*
* simple-keyboard v2.7.7
* simple-keyboard v2.18.1
* https://github.com/hodgef/simple-keyboard
*
* Copyright (c) Francisco Hodge (https://github.com/hodgef)
@@ -8,6 +8,5 @@
* 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;-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*/
*/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}
/*# sourceMappingURL=index.css.map */

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,135 @@
/*!
*
* simple-keyboard v2.18.1 (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;
}

229
build/index.d.ts vendored Normal file
View File

@@ -0,0 +1,229 @@
declare module 'simple-keyboard' {
interface KeyboardLayoutObject {
default: string[];
shift?: string[];
}
interface KeyboardButtonTheme {
class: string;
buttons: string;
}
interface KeyboardOptions {
/**
* Utilities
*/
utilities?: any;
/**
* 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;
/**
* Opt out of PointerEvents handling, falling back to the prior mouse event logic.
*/
useMouseEvents?: 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;
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1598
build/simple-keyboard.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -27,24 +27,27 @@ var dotenvFiles = [
// Load environment variables from .env* files. Suppress warnings using silent
// if this file is missing. dotenv will never modify any environment variables
// that have already been set.
// that have already been set. Variable expansion is supported in .env files.
// https://github.com/motdotla/dotenv
// https://github.com/motdotla/dotenv-expand
dotenvFiles.forEach(dotenvFile => {
if (fs.existsSync(dotenvFile)) {
require('dotenv').config({
path: dotenvFile,
});
require('dotenv-expand')(
require('dotenv').config({
path: dotenvFile,
})
);
}
});
// We support resolving modules according to `NODE_PATH`.
// This lets you use absolute paths in imports inside large monorepos:
// https://github.com/facebookincubator/create-react-app/issues/253.
// https://github.com/facebook/create-react-app/issues/253.
// It works similar to `NODE_PATH` in Node itself:
// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims.
// https://github.com/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.
const appDirectory = fs.realpathSync(process.cwd());
process.env.NODE_PATH = (process.env.NODE_PATH || '')

View File

@@ -1,7 +1,7 @@
'use strict';
// 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 = {
process() {

View File

@@ -3,10 +3,28 @@
const path = require('path');
// 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 = {
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};`;
},
};

View File

@@ -5,20 +5,20 @@ const fs = require('fs');
const url = require('url');
// 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 resolveApp = relativePath => path.resolve(appDirectory, relativePath);
const envPublicUrl = process.env.PUBLIC_URL;
function ensureSlash(path, needsSlash) {
const hasSlash = path.endsWith('/');
function ensureSlash(inputPath, needsSlash) {
const hasSlash = inputPath.endsWith('/');
if (hasSlash && !needsSlash) {
return path.substr(path, path.length - 1);
return inputPath.substr(0, inputPath.length - 1);
} else if (!hasSlash && needsSlash) {
return `${path}/`;
return `${inputPath}/`;
} else {
return path;
return inputPath;
}
}
@@ -38,27 +38,57 @@ function getServedPath(appPackageJson) {
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/
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
appBuild: resolveApp('build'),
appDemoBuild: resolveApp('demo'),
appDemo: resolveApp('demo'),
appPublic: resolveApp('public'),
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'),
appSrc: resolveApp('src'),
appSrcLib: resolveApp('src/lib'),
appSrcLibTypes: resolveApp('src/lib/@types'),
appSrcDemo: resolveApp('src/demo'),
appTsConfig: resolveApp('tsconfig.json'),
yarnLockFile: resolveApp('yarn.lock'),
testsSetup: resolveApp('src/setupTests.js'),
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
publicUrl: getPublicUrl(resolveApp('package.json')),
servedPath: getServedPath(resolveApp('package.json')),
// 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'),
//servedPath: getServedPath(resolveApp('package.json')),
servedPath: ''
};
module.exports.moduleFileExtensions = moduleFileExtensions;

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);
}

View File

@@ -1,16 +1,27 @@
'use strict';
const autoprefixer = require('autoprefixer');
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 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 ExtractTextPlugin = require('extract-text-webpack-plugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const WorkboxWebpackPlugin = require('workbox-webpack-plugin');
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');
// 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.
@@ -20,10 +31,13 @@ const publicPath = paths.servedPath;
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 = '.';
const publicUrl = publicPath.slice(0, -1);
// Get environment variables to inject into our app.
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.');
}
// Note: defined here because it will be used more than once.
const cssFilename = 'css/index.css';
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
// ExtractTextPlugin expects the build output to be flat.
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
// However, our output is structured with css, js and media folders.
// To have this structure working with relative paths, we have to use custom options.
const extractTextPluginOptions = shouldUseRelativeAssetPaths
? // Making sure that the publicPath goes back to to build folder.
{ publicPath: Array(cssFilename.split('/').length).join('../') }
: {};
// 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 = [
{
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: shouldUseSourceMap ? 'source-map' : false,
// 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: {
// CRL: Updated whole block with library specific info
path: paths.appDemoBuild,
// The build folder.
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',
libraryTarget: 'umd',
library: 'SimpleKeyboard'
chunkFilename: 'index.[chunkhash:8].chunk.js',
// 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: {
// 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/facebookincubator/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat(
// 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/facebookincubator/create-react-app/issues/290
// https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// 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: {
// 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'
'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.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: {
strictExportPresence: true,
rules: [
// TODO: 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 } },
// 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|jsx|mjs)$/,
test: /\.(js|mjs|jsx)$/,
enforce: 'pre',
use: [
{
options: {
formatter: eslintFormatter,
eslintPath: require.resolve('eslint')
formatter: require.resolve('react-dev-utils/eslintFormatter'),
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
@@ -128,76 +282,132 @@ module.exports = {
loader: require.resolve('url-loader'),
options: {
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)$/,
include: [paths.appLibSrc, paths.appDemoSrc], // CRL: updated with library src folder
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: paths.appSrcDemo,
loader: require.resolve('babel-loader'),
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.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader normally turns CSS into JS modules injecting <style>,
// but unlike in development configuration, we do something different.
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders
// (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.
// `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: /\.css$/,
loader: ExtractTextPlugin.extract(
Object.assign(
{
fallback: {
loader: require.resolve('style-loader'),
options: {
hmr: false
}
},
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap
}
},
{
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'
})
]
}
}
]
},
extractTextPluginOptions
)
)
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
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.
@@ -206,68 +416,120 @@ module.exports = {
{
loader: require.resolve('file-loader'),
// 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
// by webpacks internal loaders.
exclude: [/\.js$/, /\.html$/, /\.json$/],
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: {
name: 'media/[name].[ext]'
}
}
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 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),
// Minify the code.
new webpack.optimize.UglifyJsPlugin({
compress: 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
}),
// Note: this won't work without ExtractTextPlugin.extract(..) in `loaders`.
new ExtractTextPlugin({
filename: cssFilename
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'index.css',
chunkFilename: 'index.[contenthash:8].chunk.css',
}),
// Generate 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$/),
new CopyWebpackPlugin([
{ from: `${paths.appPublic}/favicon.ico`, to: paths.appDemoBuild },
{ from: `${paths.appPublic}/manifest.json`, to: paths.appDemoBuild }
])
],
// CRL: added externals block for library
// externals: {
// 'react': 'react',
// 'react-dom': 'react-dom'
// },
// 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.appSrcDemo,
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: {
@@ -275,6 +537,9 @@ module.exports = {
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
child_process: 'empty',
},
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
};

View File

@@ -1,16 +1,23 @@
'use strict';
const autoprefixer = require('autoprefixer');
const fs = require('fs');
const path = require('path');
const resolve = require('resolve');
const webpack = require('webpack');
const PnpWebpackPlugin = require('pnp-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin');
const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin');
const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent');
const getClientEnvironment = require('./env');
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.
// 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.
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.
// It is focused on developer experience and fast rebuilds.
// The production configuration is different and lives in a separate file.
module.exports = {
mode: 'development',
// 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',
// These are the "entry points" to our application.
// 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: [
// Include an alternative client for WebpackDevServer. A client's job is to
// connect to WebpackDevServer by a socket and get notified about changes.
@@ -65,29 +116,46 @@ module.exports = {
devtoolModuleFilenameTemplate: info =>
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: {
// 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/facebookincubator/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat(
// 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/facebookincubator/create-react-app/issues/290
// https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// 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: {
// 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,
@@ -96,24 +164,30 @@ module.exports = {
new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
],
},
resolveLoader: {
plugins: [
// Also related to Plug'n'Play, but this time it tells Webpack to load its loaders
// from the current package.
PnpWebpackPlugin.moduleLoader(module),
],
},
module: {
strictExportPresence: true,
rules: [
// TODO: 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 } },
// 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|jsx|mjs)$/,
test: /\.(js|mjs|jsx)$/,
enforce: 'pre',
use: [
{
options: {
formatter: eslintFormatter,
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
},
loader: require.resolve('eslint-loader'),
},
@@ -136,16 +210,62 @@ module.exports = {
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,
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]',
},
},
},
],
],
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
// 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.
@@ -153,37 +273,46 @@ module.exports = {
// "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 development "style" loader enables hot editing of CSS.
// By default we support CSS Modules with the extension .module.css
{
test: /\.css$/,
use: [
require.resolve('style-loader'),
test: cssRegex,
exclude: cssModuleRegex,
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'),
options: {
importLoaders: 1,
},
importLoaders: 2,
modules: true,
getLocalIdent: getCSSModuleLocalIdent,
},
{
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',
}),
],
},
},
],
'sass-loader'
),
},
// "file" loader makes sure those assets get served by WebpackDevServer.
// When you `import` an asset, you get its (virtual) filename.
@@ -192,10 +321,10 @@ module.exports = {
// that fall through the other loaders.
{
// 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
// by webpacks internal loaders.
exclude: [/\.js$/, /\.html$/, /\.json$/],
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
loader: require.resolve('file-loader'),
options: {
name: 'static/media/[name].[hash:8].[ext]',
@@ -208,18 +337,19 @@ module.exports = {
],
},
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.
new HtmlWebpackPlugin({
inject: true,
template: paths.appHtml,
}),
// Add module names to factory functions so they appear in browser profiler.
new webpack.NamedModulesPlugin(),
// 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(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 === 'development') { ... }. See `./env.js`.
new webpack.DefinePlugin(env.stringified),
@@ -227,12 +357,12 @@ module.exports = {
new webpack.HotModuleReplacementPlugin(),
// Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this.
// See https://github.com/facebookincubator/create-react-app/issues/240
// See https://github.com/facebook/create-react-app/issues/240
new CaseSensitivePathsPlugin(),
// If you require a missing module and then `npm install` it, you still have
// to restart the development server for Webpack to discover it. This plugin
// makes the discovery automatic so you don't have to restart.
// See https://github.com/facebookincubator/create-react-app/issues/186
// See https://github.com/facebook/create-react-app/issues/186
new WatchMissingNodeModulesPlugin(paths.appNodeModules),
// 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
@@ -240,7 +370,44 @@ module.exports = {
// 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 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.
// Tell Webpack to provide empty mocks for them so importing them works.
node: {
@@ -250,10 +417,7 @@ module.exports = {
tls: 'empty',
child_process: 'empty',
},
// Turn off performance hints during development because we don't do any
// splitting or minification in interest of speed. These warnings become
// cumbersome.
performance: {
hints: false,
},
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
};

View File

@@ -1,14 +1,27 @@
'use strict';
const autoprefixer = require('autoprefixer');
const fs = require('fs');
const path = require('path');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const eslintFormatter = require('react-dev-utils/eslintFormatter');
const resolve = require('resolve');
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 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 CopyWebpackPlugin = require('copy-webpack-plugin');
const getPackageJson = require('./getPackageJson');
var PrettierPlugin = require("prettier-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.
@@ -18,6 +31,9 @@ const publicPath = paths.servedPath;
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.
@@ -31,17 +47,14 @@ if (env.stringified['process.env'].NODE_ENV !== '"production"') {
throw new Error('Production builds must have NODE_ENV=production.');
}
// Note: defined here because it will be used more than once.
const cssFilename = 'css/index.css';
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
// ExtractTextPlugin expects the build output to be flat.
// (See https://github.com/webpack-contrib/extract-text-webpack-plugin/issues/27)
// However, our output is structured with css, js and media folders.
// To have this structure working with relative paths, we have to use custom options.
const extractTextPluginOptions = shouldUseRelativeAssetPaths
? // Making sure that the publicPath goes back to to build folder.
{ publicPath: Array(cssFilename.split('/').length).join('../') }
: {};
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
const {
version,
@@ -61,78 +74,228 @@ const banner = `
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: shouldUseSourceMap ? 'source-map' : false,
// 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: {
// CRL: Updated whole block with library specific info
// 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: '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',
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: {
// 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/facebookincubator/create-react-app/issues/253
modules: ['node_modules', paths.appNodeModules].concat(
// 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/facebookincubator/create-react-app/issues/290
// https://github.com/facebook/create-react-app/issues/290
// `web` extension prefixes have been added for better support
// 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: {
// 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.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: {
strictExportPresence: true,
rules: [
// TODO: 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 } },
// 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|jsx|mjs)$/,
test: /\.(js|mjs|jsx)$/,
enforce: 'pre',
use: [
{
options: {
formatter: eslintFormatter,
formatter: require.resolve('react-dev-utils/eslintFormatter'),
eslintPath: require.resolve('eslint'),
},
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
@@ -146,77 +309,129 @@ module.exports = {
loader: require.resolve('url-loader'),
options: {
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)$/,
include: paths.appLibSrc, // CRL: updated with library src folder
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,
},
},
// 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.
// "css" loader resolves paths in CSS and adds assets as dependencies.
// "style" loader normally turns CSS into JS modules injecting <style>,
// but unlike in development configuration, we do something different.
// `ExtractTextPlugin` first applies the "postcss" and "css" loaders
// (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.
// `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: /\.css$/,
loader: ExtractTextPlugin.extract(
Object.assign(
{
fallback: {
loader: require.resolve('style-loader'),
options: {
hmr: false,
},
},
use: [
{
loader: require.resolve('css-loader'),
options: {
importLoaders: 1,
minimize: true,
sourceMap: shouldUseSourceMap,
},
},
{
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',
}),
],
},
},
],
},
extractTextPluginOptions
)
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'
),
// Note: this won't work without `new ExtractTextPlugin()` in `plugins`.
},
// "file" loader makes sure assets end up in the `build` folder.
// When you `import` an asset, you get its filename.
@@ -225,12 +440,12 @@ module.exports = {
{
loader: require.resolve('file-loader'),
// 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
// by webpacks internal loaders.
exclude: [/\.js$/, /\.html$/, /\.json$/],
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
options: {
name: 'media/[name].[ext]',
name: 'static/media/[name].[hash:8].[ext]',
},
},
// ** STOP ** Are you adding a new loader?
@@ -240,52 +455,112 @@ module.exports = {
],
},
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),
// Minify the code.
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
// 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,
new MiniCssExtractPlugin({
// Options similar to the same options in webpackOptions.output
// both options are optional
filename: 'css/index.css',
chunkFilename: 'css/index.[contenthash:8].chunk.css',
}),
// Generate 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$/),
],
// CRL: added externals block for library
externals: {
'react': 'react',
'react-dom': 'react-dom'
},
new CopyWebpackPlugin([
{
from: `${paths.appSrcLibTypes}`,
to: paths.appBuild
}
]),
new PrettierPlugin(),
// Generate a service worker script that will precache, and keep up to date,
// the HTML & assets that are part of the Webpack build.
/*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: {
@@ -295,4 +570,7 @@ module.exports = {
tls: 'empty',
child_process: 'empty',
},
// Turn off performance processing because we utilize
// our own hints via the FileSizeReporter
performance: false,
};

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,
};

View File

@@ -1,10 +1,12 @@
'use strict';
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
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 paths = require('./paths');
const fs = require('fs');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
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
// However, it made several existing use cases such as development in cloud
// environment or subdomains in development significantly more complicated:
// https://github.com/facebookincubator/create-react-app/issues/2271
// https://github.com/facebookincubator/create-react-app/issues/2233
// https://github.com/facebook/create-react-app/issues/2271
// https://github.com/facebook/create-react-app/issues/2233
// While we're investigating better solutions, for now we will take a
// compromise. Since our WDS configuration only serves files in the `public`
// folder we won't consider accessing them a vulnerability. However, if you
@@ -61,39 +63,42 @@ module.exports = function(proxy, allowedHost) {
// as we specified in the config. In development, we always serve from /.
publicPath: config.output.publicPath,
// 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,
// 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
// https://github.com/facebookincubator/create-react-app/issues/1065
// https://github.com/facebook/create-react-app/issues/1065
watchOptions: {
ignored: new RegExp(
`^(?!${path
.normalize(paths.appSrc + '/')
.replace(/[\\]+/g, '\\\\')}).+[\\\\/]node_modules[\\\\/]`,
'g'
),
ignored: ignoredFiles(paths.appSrc),
},
// Enable HTTPS if the HTTPS environment variable is set to 'true'
https: protocol === 'https',
host: host,
host,
overlay: false,
historyApiFallback: {
// 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,
},
public: allowedHost,
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.
app.use(errorOverlayMiddleware());
// This service worker file is effectively a 'no-op' that will reset any
// previous service worker registered for the same host:port combination.
// We do this in development to avoid hitting the production cache if
// it used the same host and port.
// https://github.com/facebookincubator/create-react-app/issues/2272#issuecomment-302832432
// https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
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

View File

@@ -175,8 +175,7 @@ This class:</p>
<div>
<div data-ice="description"><p>Tracks current cursor position
As keys are pressed, text will be added/removed at that position within the input.</p>
<div data-ice="description"><p>Only first instance should insall the caret handling events</p>
</div>
</div>
</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 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>
</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 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>
</h3>
<div data-ice="description"><p>Tracks current cursor position
As keys are pressed, text will be added/removed at that position within the input.</p>
<div data-ice="description"><p>Only first instance should insall the caret handling events</p>
</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 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>
</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 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>
</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 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>
</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 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>
</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 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>
</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 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>
</h3>
@@ -1731,7 +1729,7 @@ keyboard.clearInput();</code></pre>
<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>
</h3>
@@ -1818,7 +1816,7 @@ keyboard.clearInput();</code></pre>
<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>
</h3>
@@ -1861,7 +1859,7 @@ keyboard.clearInput();</code></pre>
<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>
</h3>
@@ -1904,7 +1902,7 @@ keyboard.clearInput();</code></pre>
<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>
</h3>
@@ -1948,7 +1946,7 @@ keyboard.clearInput();</code></pre>
<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>
</h3>
@@ -2065,6 +2063,13 @@ keyboard.clearInput();</code></pre>
<td data-ice="description"><p>Enable highlighting of keys pressed on physical keyboard.</p>
</td>
</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">
<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>
@@ -2148,7 +2153,7 @@ keyboard.clearInput();</code></pre>
<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>
</h3>
@@ -2192,7 +2197,7 @@ keyboard.clearInput();</code></pre>
<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>
</h3>
@@ -2236,7 +2241,7 @@ keyboard.clearInput();</code></pre>
<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>
</h3>
@@ -2327,7 +2332,7 @@ keyboard.clearInput();</code></pre>
<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>
</h3>
@@ -2407,7 +2412,7 @@ keyboard.clearInput();</code></pre>
<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>
</h3>
@@ -2467,7 +2472,7 @@ keyboard.clearInput();</code></pre>
<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>
</h3>
@@ -2512,7 +2517,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -2573,7 +2578,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -2646,7 +2651,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -2719,7 +2724,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -2792,7 +2797,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -2865,7 +2870,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -2925,7 +2930,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -2991,7 +2996,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -3035,7 +3040,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -3079,7 +3084,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -3123,7 +3128,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -3167,7 +3172,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -3211,7 +3216,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -3255,7 +3260,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -3335,7 +3340,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -3379,7 +3384,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -3440,7 +3445,7 @@ Used interally between re-renders.</p>
<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>
</h3>
@@ -3508,7 +3513,7 @@ Used interally between re-renders.</p>
<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>
</h3>

View File

@@ -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>
</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>
</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>
</div>
<div>
@@ -501,6 +501,35 @@
<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>
</tr>
</tbody>
@@ -572,7 +601,7 @@
<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>
</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 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>
</h3>
@@ -702,6 +731,13 @@
<td data-ice="description"><p>The (cursor) position where the string should be added</p>
</td>
</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>
</table>
</div>
@@ -748,7 +784,7 @@
<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>
</h3>
@@ -821,7 +857,7 @@
<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>
</h3>
@@ -901,7 +937,7 @@
<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>
</h3>
@@ -975,7 +1011,7 @@
<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>
</h3>
@@ -1062,7 +1098,7 @@
<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>
</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 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>
</h3>
@@ -1166,6 +1202,13 @@
<td data-ice="description"><p>The cursor&apos;s current position</p>
</td>
</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>
</table>
</div>
@@ -1212,7 +1255,7 @@
<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>
</h3>
@@ -1296,7 +1339,7 @@
<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>
</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 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>
</h3>
@@ -1386,6 +1429,13 @@
<td data-ice="description"><p>The (cursor) position from where the characters should be removed</p>
</td>
</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>
</table>
</div>
@@ -1432,7 +1482,7 @@
<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>
</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>

View File

@@ -1,7 +1,7 @@
{
"coverage": "100%",
"expectCount": 77,
"actualCount": 77,
"expectCount": 78,
"actualCount": 78,
"files": {
"src/demo/App.js": {
"expectCount": 8,
@@ -24,8 +24,8 @@
"undocumentLines": []
},
"src/lib/services/Utilities.js": {
"expectCount": 15,
"actualCount": 15,
"expectCount": 16,
"actualCount": 16,
"undocumentLines": []
},
"src/lib/tests/TestUtility.js": {

View File

@@ -68,7 +68,7 @@ class App {
* Creates a new simple-keyboard instance
*/
this.keyboard = new Keyboard({
//debug: true,
debug: true,
layoutName: this.layoutName,
onChange: input =&gt; this.onChange(input),
onKeyPress: button =&gt; this.onKeyPress(button),
@@ -85,7 +85,7 @@ class App {
&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);
});
}

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 {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} 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} 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(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).
@@ -104,6 +105,7 @@ class SimpleKeyboard {
this.options.layoutName = this.options.layoutName || &quot;default&quot;;
this.options.theme = this.options.theme || &quot;hg-theme-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
@@ -215,7 +217,9 @@ class SimpleKeyboard {
if(!this.input[this.options.inputName])
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){
@@ -226,7 +230,9 @@ class SimpleKeyboard {
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)
console.log(&apos;Input changed:&apos;, this.input);
@@ -301,12 +307,19 @@ class SimpleKeyboard {
*/
/* istanbul ignore next */
handleButtonHold(button){
if(this.holdInteractionTimeout)
clearTimeout(this.holdInteractionTimeout);
/**
* @type {object} Timeout dictating the speed of key hold iterations
*/
this.holdInteractionTimeout = setTimeout(() =&gt; {
this.handleButtonClicked(button);
this.handleButtonHold(button);
if(this.isMouseHold){
this.handleButtonClicked(button);
this.handleButtonHold(button);
} else {
clearTimeout(this.holdInteractionTimeout);
}
}, 100);
}
@@ -325,7 +338,7 @@ class SimpleKeyboard {
*/
clearInput(inputName){
inputName = inputName || this.options.inputName;
this.input[this.options.inputName] = &apos;&apos;;
this.input[inputName] = &apos;&apos;;
/**
* Enforce syncInstanceInputs, if set
@@ -535,35 +548,59 @@ class SimpleKeyboard {
* Retrieves the current cursor position within a input or textarea (if any)
*/
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){
console.log(&quot;Caret handling started&quot;);
console.log(`Caret handling started (${this.keyboardDOMClass})`)
}
document.addEventListener(&quot;keyup&quot;, this.caretEventHandler);
document.addEventListener(&quot;mouseup&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
*/
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(
targetTagName === &quot;textarea&quot; ||
targetTagName === &quot;input&quot;
(targetTagName === &quot;textarea&quot; ||
targetTagName === &quot;input&quot;) &amp;&amp;
!instance.options.disableCaretPositioning
){
/**
* Tracks current cursor position
* 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){
console.log(&apos;Caret at: &apos;, event.target.selectionStart, event.target.tagName.toLowerCase());
if(instance.options.debug){
console.log(&apos;Caret at: &apos;, event.target.selectionStart, event.target.tagName.toLowerCase(), `(${instance.keyboardDOMClass})`);
}
}
});
}
/**
@@ -571,7 +608,7 @@ class SimpleKeyboard {
*/
onInit(){
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 layout = this.options.layout || KeyboardLayout.getDefaultLayout();
let useTouchEvents = this.options.useTouchEvents || false
/**
* Account for buttonTheme, if set
@@ -726,9 +764,25 @@ class SimpleKeyboard {
*/
var buttonDOM = document.createElement(&apos;div&apos;);
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
*/
@@ -788,7 +842,9 @@ class SimpleKeyboard {
/**
* Handling mouseup
*/
document.onmouseup = () =&gt; this.handleButtonMouseUp();
if (!useTouchEvents) {
document.onmouseup = () =&gt; this.handleButtonMouseUp();
}
/**
* Calling onInit

View File

@@ -93,7 +93,7 @@ class PhysicalKeyboard {
this.simpleKeyboardInstance.dispatch(instance =&gt; {
let buttonDOM = instance.getButtonElement(buttonPressed) || instance.getButtonElement(`{${buttonPressed}}`);
if(buttonDOM){
if(buttonDOM &amp;&amp; buttonDOM.removeAttribute){
buttonDOM.removeAttribute(&quot;style&quot;);
}
});

View File

@@ -60,6 +60,7 @@ class Utilities {
this.getButtonDisplayName = this.getButtonDisplayName.bind(this);
this.getUpdatedInput = this.getUpdatedInput.bind(this);
this.updateCaretPos = this.updateCaretPos.bind(this);
this.updateCaretPosAction = this.updateCaretPosAction.bind(this);
this.isMaxLengthReached = this.isMaxLengthReached.bind(this);
this.camelCase = this.camelCase.bind(this);
this.countInArray = this.countInArray.bind(this);
@@ -173,48 +174,51 @@ class Utilities {
* @param {string} input The input string
* @param {object} options The simple-keyboard options object
* @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;
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;)
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)){
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)
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]))){
output = this.addStringAt(output, button[button.length - 2], caretPos);
}
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;)
output = this.addStringAt(output, &apos;*&apos;, caretPos);
output = this.addStringAt(output, &apos;*&apos;, caretPos, moveCaret);
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;)
output = this.addStringAt(output, &apos;+&apos;, caretPos);
output = this.addStringAt(output, &apos;+&apos;, caretPos, moveCaret);
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;)
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;))
output = this.addStringAt(output, button, caretPos);
output = this.addStringAt(output, button, caretPos, moveCaret);
return output;
}
/**
* 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.
*/
updateCaretPos(length, minus){
if(minus){
if(this.simpleKeyboardInstance.caretPosition &gt; 0)
this.simpleKeyboardInstance.caretPosition = this.simpleKeyboardInstance.caretPosition - length
if(this.simpleKeyboardInstance.options.syncInstanceInputs){
this.simpleKeyboardInstance.dispatch(instance =&gt; {
this.updateCaretPosAction(instance, length, minus);
});
} 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} string The string to add
* @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;
if(this.simpleKeyboardInstance.options.debug){
console.log(&quot;Caret at:&quot;, position);
}
if(!position &amp;&amp; position !== 0){
output = source + string;
} else {
@@ -253,7 +275,7 @@ class Utilities {
* Avoid caret position change when maxLength is set
*/
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 {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){
return source;
}
@@ -287,10 +310,10 @@ class Utilities {
if(emojiMatched){
output = source.substr(0, (position - 2)) + source.substr(position);
this.updateCaretPos(2, true);
if(moveCaret) this.updateCaretPos(2, true);
} else {
output = source.substr(0, (position - 1)) + source.substr(position);
this.updateCaretPos(1, true);
if(moveCaret) this.updateCaretPos(1, true);
}
} else {
prevTwoChars = source.slice(-2);
@@ -298,10 +321,10 @@ class Utilities {
if(emojiMatched){
output = source.slice(0, -2);
this.updateCaretPos(2, true);
if(moveCaret) this.updateCaretPos(2, true);
} else {
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 condition = currentInput.length === maxLength;
if(
/**
* If pressing this button won&apos;t add more characters
@@ -389,7 +411,6 @@ class Utilities {
countInArray(array, value){
return array.reduce((n, x) =&gt; n + (x === value), 0);
}
}
export default Utilities;</code></pre>

File diff suppressed because one or more lines are too long

View File

@@ -755,6 +755,12 @@ window.esdocSearchIndex = [
"src/lib/services/Utilities.js~Utilities#updateCaretPos",
"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",
"file/src/lib/tests/TestUtility.js.html",

View File

@@ -39,7 +39,7 @@
</div>
</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">
<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="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 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="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 data-ice="file">
<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 style="display: none;" data-ice="size">70 byte</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 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="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 style="display: none;" data-ice="size">24443 byte</td>
<td style="display: none;" data-ice="lines">759</td>
<td style="display: none;" data-ice="updated">2018-11-05 16:42:42 (UTC)</td>
<td style="display: none;" data-ice="size">26391 byte</td>
<td style="display: none;" data-ice="lines">815</td>
<td style="display: none;" data-ice="updated">2018-11-01 01:52:55 (UTC)</td>
</tr>
<tr data-ice="file">
<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 style="display: none;" data-ice="size">85 byte</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 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>
@@ -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 style="display: none;" data-ice="size">746 byte</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 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="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 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="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 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="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 style="display: none;" data-ice="size">10567 byte</td>
<td style="display: none;" data-ice="lines">352</td>
<td style="display: none;" data-ice="updated">2018-10-03 17:11:36 (UTC)</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">11708 byte</td>
<td style="display: none;" data-ice="lines">373</td>
<td style="display: none;" data-ice="updated">2018-11-01 01:55:18 (UTC)</td>
</tr>
<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>
@@ -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 style="display: none;" data-ice="size">2830 byte</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>
</tbody>
</table>

16557
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,16 @@
{
"name": "simple-keyboard",
"version": "2.7.7",
"version": "2.19.0",
"description": "On-screen Javascript Virtual Keyboard",
"main": "build/index.js",
"types": "build/index.d.ts",
"scripts": {
"start": "node scripts/start.js",
"build": "node scripts/build.js",
"build": "node scripts/build.js && node scripts/buildUnminified.js",
"demo": "node scripts/demo.js",
"test": "node scripts/test.js --env=jsdom",
"test": "node scripts/test.js",
"postinstall": "node bin/postinstall",
"prepublish": "npm run build",
"prepare": "npm run build",
"docs": "esdoc"
},
"repository": {
@@ -34,87 +35,121 @@
"touchscreen",
"touch-screen",
"kiosk",
"osk"
"osk",
"js"
],
"license": "MIT",
"dependencies": {},
"devDependencies": {
"autoprefixer": "7.1.6",
"babel-core": "^6.26.3",
"babel-eslint": "7.2.3",
"babel-jest": "20.0.3",
"babel-loader": "7.1.2",
"babel-preset-react-app": "^3.1.2",
"babel-runtime": "6.26.0",
"case-sensitive-paths-webpack-plugin": "2.1.1",
"chalk": "1.1.3",
"copy-webpack-plugin": "^4.3.1",
"css-loader": "0.28.7",
"dotenv": "4.0.0",
"@babel/core": "7.3.4",
"@babel/plugin-proposal-class-properties": "^7.3.4",
"@babel/preset-env": "^7.3.4",
"@babel/preset-react": "^7.0.0",
"@svgr/webpack": "4.1.0",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.0.1",
"babel-jest": "23.6.0",
"babel-loader": "8.0.5",
"babel-plugin-named-asset-import": "^0.3.1",
"babel-preset-react-app": "^7.0.1",
"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-ecmascript-proposal-plugin": "^1.0.0",
"esdoc-standard-plugin": "^1.0.0",
"eslint": "4.10.0",
"eslint-config-react-app": "^2.0.1",
"eslint-loader": "1.9.0",
"eslint-plugin-flowtype": "2.39.1",
"eslint-plugin-import": "2.8.0",
"eslint-plugin-jsx-a11y": "5.1.1",
"eslint-plugin-react": "7.4.0",
"extract-text-webpack-plugin": "3.0.2",
"file-loader": "1.1.5",
"fs-extra": "3.0.1",
"html-webpack-plugin": "2.29.0",
"istanbul": "^0.4.5",
"istanbul-api": "^2.0.6",
"istanbul-reports": "^2.0.1",
"jest": "20.0.4",
"object-assign": "4.1.1",
"postcss-flexbugs-fixes": "3.2.0",
"postcss-loader": "2.0.8",
"promise": "8.0.1",
"prop-types": "^15.6.1",
"raf": "3.4.0",
"react": "^16.2.0",
"react-dev-utils": "^4.2.2",
"react-dom": "^16.2.0",
"style-loader": "0.19.0",
"sw-precache-webpack-plugin": "0.11.4",
"url-loader": "0.6.2",
"webpack": "3.8.1",
"webpack-dev-server": "2.9.4",
"webpack-manifest-plugin": "1.3.2",
"whatwg-fetch": "2.0.3"
"eslint": "5.15.0",
"eslint-config-react-app": "^3.0.7",
"eslint-loader": "2.1.2",
"eslint-plugin-flowtype": "3.4.2",
"eslint-plugin-import": "2.16.0",
"eslint-plugin-jsx-a11y": "6.2.1",
"eslint-plugin-react": "7.12.4",
"file-loader": "3.0.1",
"fork-ts-checker-webpack-plugin-alt": "0.4.14",
"fs-extra": "7.0.1",
"html-webpack-plugin": "4.0.0-beta.5",
"identity-obj-proxy": "3.0.0",
"jest": "23.6.0",
"jest-pnp-resolver": "1.0.2",
"jest-resolve": "23.6.0",
"mini-css-extract-plugin": "0.5.0",
"optimize-css-assets-webpack-plugin": "5.0.1",
"pnp-webpack-plugin": "1.3.1",
"postcss-flexbugs-fixes": "4.1.0",
"postcss-loader": "3.0.0",
"postcss-preset-env": "6.6.0",
"postcss-safe-parser": "4.0.1",
"prettier": "^1.16.4",
"prettier-webpack-plugin": "^1.2.0",
"react": "^16.8.3",
"react-app-polyfill": "^0.2.1",
"react-dev-utils": "^7.0.3",
"react-dom": "^16.8.3",
"resolve": "1.10.0",
"sass-loader": "7.1.0",
"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": {
"collectCoverageFrom": [
"src/**/*.{js,jsx,mjs}",
"src/**/*.{js,jsx,ts,tsx}",
"!src/**/*.d.ts",
"!**/tests/**"
],
"resolver": "jest-pnp-resolver",
"setupFiles": [
"<rootDir>/config/polyfills.js"
"react-app-polyfill/jsdom"
],
"testMatch": [
"<rootDir>/src/**/__tests__/**/*.{js,jsx,mjs}",
"<rootDir>/src/**/?(*.)(spec|test).{js,jsx,mjs}"
"<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
"<rootDir>/src/**/?(*.)(spec|test).{js,jsx,ts,tsx}"
],
"testEnvironment": "node",
"testEnvironment": "jsdom",
"testURL": "http://localhost",
"transform": {
"^.+\\.(js|jsx|mjs)$": "<rootDir>/node_modules/babel-jest",
"^.+\\.(js|jsx|ts|tsx)$": "<rootDir>/node_modules/babel-jest",
"^.+\\.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": [
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs)$"
"[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$",
"^.+\\.module\\.(css|sass|scss)$"
],
"moduleNameMapper": {
"^react-native$": "react-native-web"
"^react-native$": "react-native-web",
"^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
},
"moduleFileExtensions": [
"web.js",
"mjs",
"js",
"web.ts",
"ts",
"web.tsx",
"tsx",
"json",
"web.jsx",
"jsx",
@@ -123,10 +158,13 @@
},
"babel": {
"presets": [
"react-app"
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-proposal-class-properties"
]
]
},
"eslintConfig": {
"extends": "react-app"
}
}

View File

@@ -14,37 +14,55 @@ process.on('unhandledRejection', 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.prod');
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.appLibIndexJs])) { // CRL: Updated with library index file
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
// First, read the current file sizes in build directory.
// This lets us display how much they changed later.
measureFileSizesBeforeBuild(paths.appBuild)
// 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);
})
@@ -76,13 +94,31 @@ measureFileSizesBeforeBuild(paths.appBuild)
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) {
@@ -91,10 +127,20 @@ function build(previousFileSizes) {
let compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
let messages;
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) {
// Only keep the first error. Others are often indicative
// 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 resolve({
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,
});
}

189
scripts/buildUnminified.js Normal file
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,
});
}

View File

@@ -14,37 +14,55 @@ process.on('unhandledRejection', 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.demo');
const paths = require('../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
const printBuildError = require('react-dev-utils/printBuildError');
const measureFileSizesBeforeBuild =
FileSizeReporter.measureFileSizesBeforeBuild;
const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
const useYarn = fs.existsSync(paths.yarnLockFile);
// These sizes are pretty large. We'll warn for bundles exceeding them.
const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appDemoIndexJs])) { // CRL: Updated with library index file
if (!checkRequiredFiles([paths.appHtml, paths.appDemoIndexJs])) {
process.exit(1);
}
// First, read the current file sizes in build directory.
// This lets us display how much they changed later.
measureFileSizesBeforeBuild(paths.appDemoBuild)
// 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.appDemo);
})
.then(previousFileSizes => {
// Remove all content but keep the directory so that
// if you're in it, you don't end up in Trash
fs.emptyDirSync(paths.appDemoBuild);
fs.emptyDirSync(paths.appDemo);
// Merge with the public folder
copyPublicFolder();
// Start the webpack build
return build(previousFileSizes);
})
@@ -71,30 +89,58 @@ measureFileSizesBeforeBuild(paths.appDemoBuild)
printFileSizesAfterBuild(
stats,
previousFileSizes,
paths.appDemoBuild,
paths.appDemo,
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.appDemo);
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 build of the demo app...');
console.log('Creating an optimized production build...');
let compiler = webpack(config);
return new Promise((resolve, reject) => {
compiler.run((err, stats) => {
let messages;
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) {
// Only keep the first error. Others are often indicative
// 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 resolve({
const resolveArgs = {
stats,
previousFileSizes,
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,
});
}

View File

@@ -14,6 +14,7 @@ process.on('unhandledRejection', err => {
// Ensure environment variables are read.
require('../config/env');
const fs = require('fs');
const chalk = require('chalk');
const webpack = require('webpack');
@@ -35,7 +36,7 @@ const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;
// Warn and crash if required files are missing
if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
if (!checkRequiredFiles([paths.appHtml, paths.appDemoIndexJs])) {
process.exit(1);
}
@@ -43,9 +44,32 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
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
// run on a different port. `detect()` Promise resolves to the next free port.
choosePort(HOST, DEFAULT_PORT)
if (process.env.HOST) {
console.log(
chalk.cyan(
`Attempting to bind to HOST environment variable: ${chalk.yellow(
chalk.bold(process.env.HOST)
)}`
)
);
console.log(
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
);
console.log(
`Learn more here: ${chalk.yellow('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 => {
if (port == null) {
// We have not found a port.
@@ -59,7 +83,7 @@ choosePort(HOST, DEFAULT_PORT)
// Load proxy config
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
// Serve webpack assets generated by the compiler over a web sever.
// Serve webpack assets generated by the compiler over a web server.
const serverConfig = createDevServerConfig(
proxyConfig,
urls.lanUrlForConfig

View File

@@ -15,12 +15,38 @@ process.on('unhandledRejection', err => {
// Ensure environment variables are read.
require('../config/env');
const jest = require('jest');
const argv = process.argv.slice(2);
// Watch unless on CI or in coverage mode
if (!process.env.CI && argv.indexOf('--coverage') < 0) {
argv.push('--watch');
const jest = require('jest');
const execSync = require('child_process').execSync;
let argv = process.argv.slice(2);
function isInGitRepository() {
try {
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
function isInMercurialRepository() {
try {
execSync('hg --cwd . root', { stdio: 'ignore' });
return true;
} catch (e) {
return false;
}
}
// Watch unless on CI, 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');
}

View File

@@ -31,7 +31,7 @@ class App {
onChange: input => this.onChange(input),
onKeyPress: button => this.onKeyPress(button),
newLineOnEnter: true,
physicalKeyboardHighlight: true,
physicalKeyboardHighlight: true
});
/**
@@ -43,7 +43,7 @@ class App {
</div>
`);
document.querySelector('.input').addEventListener('change', (event) => {
document.querySelector('.input').addEventListener('input', (event) => {
this.keyboard.setInput(event.target.value);
});
}

View File

@@ -8,7 +8,7 @@
#root .simple-keyboard-preview {
background: rgba(0,0,0,0.8);
border: 20px solid rgba(0,0,0,0.1);
height: 260px;
height: 200px;
border-top-right-radius: 5px;
border-top-left-radius: 5px;
padding: 10px;

View File

@@ -48,7 +48,7 @@ it('Demo input change will work', () => {
demo.onDOMLoaded();
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");
});

229
src/lib/@types/index.d.ts vendored Normal file
View File

@@ -0,0 +1,229 @@
declare module 'simple-keyboard' {
interface KeyboardLayoutObject {
default: string[];
shift?: string[];
}
interface KeyboardButtonTheme {
class: string;
buttons: string;
}
interface KeyboardOptions {
/**
* Utilities
*/
utilities?: any;
/**
* 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;
/**
* Opt out of PointerEvents handling, falling back to the prior mouse event logic.
*/
useMouseEvents?: 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;
}

View File

@@ -1,10 +1,12 @@
body, html {
body,
html {
margin: 0;
padding: 0;
}
.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%;
user-select: none;
box-sizing: border-box;
@@ -24,27 +26,39 @@ body, html {
margin-right: 5px;
}
.simple-keyboard .hg-row .hg-button-container {
margin-right: 5px;
}
.simple-keyboard .hg-row > div:last-child {
margin-right: 0;
}
.simple-keyboard .hg-row .hg-button-container {
display: flex;
}
.simple-keyboard .hg-button {
display: inline-block;
flex-grow: 1;
cursor: pointer;
}
.hg-standardBtn {
max-width: 100px;
.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);
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);
box-shadow: 0px 0px 3px -1px rgba(0, 0, 0, 0.3);
height: 40px;
border-radius: 5px;
box-sizing: border-box;
@@ -55,11 +69,18 @@ body, html {
display: flex;
align-items: 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;
}
}
.simple-keyboard.hg-theme-default.hg-layout-numeric .hg-button {
width: 33.3%;
@@ -100,4 +121,4 @@ body, html {
.simple-keyboard.hg-theme-default .hg-button.hg-standardBtn[data-skbtn="@"] {
max-width: 60px;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -45,6 +45,32 @@ it('Keyboard will run with debug option set', () => {
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', () => {
testUtil.setDOM();
let keyboard = new Keyboard({
@@ -111,7 +137,9 @@ it('Keyboard onKeyPress will work', () => {
it('Keyboard standard function buttons will not change input', () => {
testUtil.setDOM();
let keyboard = new Keyboard();
let keyboard = new Keyboard({
useButtonTag: true
});
testUtil.iterateButtons((button) => {
if(button.getAttribute("data-skbtn") === "{shift}"){
@@ -158,18 +186,20 @@ it('Keyboard onChange will work', () => {
expect(output).toBe("q");
});
it('Keyboard clearInput will work', () => {
it('Keyboard onChangeAll will work', () => {
testUtil.setDOM();
let keyboard = new Keyboard();
let output;
keyboard.input = {
"default": "hello"
};
let keyboard = new Keyboard({
onChangeAll: (input) => {
output = input ? input.default : null;
}
});
keyboard.clearInput();
keyboard.getButtonElement("q").onclick();
expect(keyboard.getInput()).toBeFalsy();
expect(output).toBe("q");
});
it('Keyboard clearInput will work', () => {
@@ -570,11 +600,49 @@ it('Keyboard caretEventHandler will detect input, textarea focus', () => {
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', () => {
testUtil.setDOM();
let keyboard = new Keyboard();
keyboard.isMouseHold = true;
keyboard.caretEventHandler({
charCode: 0,
code: "KeyF",
@@ -835,7 +903,27 @@ it('Keyboard handleButtonMouseDown will work', () => {
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();
});
@@ -861,4 +949,226 @@ it('Keyboard onModulesLoaded will work', () => {
});
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 beforeFirstRender will show PointerEvents warning', () => {
testUtil.setDOM();
let timesCalled = 0;
window.PointerEvent = window.PointerEvent ? window.PointerEvent : () => {};
let keyboard = new Keyboard({
debug: true,
beforeFirstRender: () => {
timesCalled++;
}
});
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);
});
it('Keyboard parseRowDOMContainers will work', () => {
testUtil.setDOM();
let keyboard = new Keyboard({
layout: {
'default': [
'` [1 2 3 4 5 6 7 8 9] 0 - = {bksp}',
'{tab} q w e r t y u [i o p] [ ] \\',
'{lock} [a s d] f g h j k l ; \' {enter}',
'{shift} z x c v b n m , . / {shift}',
'[.com @] {space} {arrowleft} [{arrowup} {arrowdown}] {arrowright}'
],
'shift': [
'~ ! @ # $ % ^ & * ( ) _ + {bksp}',
'{tab} [Q W E R T] Y U I O P { } |',
'{lock} A S D F G H J K L : " {enter}',
'{shift} Z X C V [B N M <] > ? {shift}',
'.com @ {space}'
]
}
});
let containers = Array.from(document.querySelectorAll(".hg-button-container"));
expect(containers.length).toBe(5);
keyboard.setOptions({
debug: true
});
expect(containers.length).toBe(5);
});
it('Keyboard parseRowDOMContainers will ignore empty rows', () => {
testUtil.setDOM();
let failed = false;
try {
let keyboard = new Keyboard();
keyboard.parseRowDOMContainers({
children: []
});
} catch (e) {
failed = true;
}
expect(failed).toBeFalsy();
});
it('Keyboard parseRowDOMContainers will ignore missing endIndex or endIndex before startIndex', () => {
testUtil.setDOM();
let keyboard = new Keyboard({
layout: {
'default': [
'` [1 2 3 4 5 6 7 8 9 0 - = {bksp}',
'` 1 2 3] 4 5 6 7 8 9 [0 - = {bksp}',
]
}
});
let containers = Array.from(document.querySelectorAll(".hg-button-container"));
expect(containers.length).toBe(0);
});
it('Keyboard disableRowButtonContainers will bypass parseRowDOMContainers', () => {
testUtil.setDOM();
let keyboard = new Keyboard({
disableRowButtonContainers: true,
layout: {
'default': [
'` [1 2 3 4 5 6 7 8 9] 0 - = {bksp}',
'{tab} q w e r t y u [i o p] [ ] \\',
'{lock} [a s d] f g h j k l ; \' {enter}',
'{shift} z x c v b n m , . / {shift}',
'[.com @] {space} {arrowleft} [{arrowup} {arrowdown}] {arrowright}'
],
'shift': [
'~ ! @ # $ % ^ & * ( ) _ + {bksp}',
'{tab} [Q W E R T] Y U I O P { } |',
'{lock} A S D F G H J K L : " {enter}',
'{shift} Z X C V [B N M <] > ? {shift}',
'.com @ {space}'
]
}
});
let containers = Array.from(document.querySelectorAll(".hg-button-container"));
expect(containers.length).toBe(0);
});

View File

@@ -1,2 +1,2 @@
import SimpleKeyboard from './components/Keyboard';
import SimpleKeyboard from "./components/Keyboard";
export default SimpleKeyboard;

View File

@@ -6,24 +6,24 @@ class KeyboardLayout {
* Get default simple-keyboard layout
* @return {object} The default layout (US-QWERTY)
*/
static getDefaultLayout(){
static getDefaultLayout() {
return {
'default': [
'` 1 2 3 4 5 6 7 8 9 0 - = {bksp}',
'{tab} q w e r t y u i o p [ ] \\',
'{lock} a s d f g h j k l ; \' {enter}',
'{shift} z x c v b n m , . / {shift}',
'.com @ {space}'
default: [
"` 1 2 3 4 5 6 7 8 9 0 - = {bksp}",
"{tab} q w e r t y u i o p [ ] \\",
"{lock} a s d f g h j k l ; ' {enter}",
"{shift} z x c v b n m , . / {shift}",
".com @ {space}"
],
'shift': [
'~ ! @ # $ % ^ & * ( ) _ + {bksp}',
'{tab} Q W E R T Y U I O P { } |',
shift: [
"~ ! @ # $ % ^ & * ( ) _ + {bksp}",
"{tab} Q W E R T Y U I O P { } |",
'{lock} A S D F G H J K L : " {enter}',
'{shift} Z X C V B N M < > ? {shift}',
'.com @ {space}'
"{shift} Z X C V B N M < > ? {shift}",
".com @ {space}"
]
}
};
}
}
export default KeyboardLayout;
export default KeyboardLayout;

View File

@@ -5,7 +5,7 @@ class PhysicalKeyboard {
/**
* Creates an instance of the PhysicalKeyboard service
*/
constructor(simpleKeyboardInstance){
constructor(simpleKeyboardInstance) {
/**
* @type {object} A simple-keyboard instance
*/
@@ -15,7 +15,9 @@ class PhysicalKeyboard {
* Bindings
*/
this.initKeyboardListener = this.initKeyboardListener.bind(this);
this.getSimpleKeyboardLayoutKey = this.getSimpleKeyboardLayoutKey.bind(this);
this.getSimpleKeyboardLayoutKey = this.getSimpleKeyboardLayoutKey.bind(
this
);
/**
* Initialize key listeners
@@ -26,32 +28,40 @@ class PhysicalKeyboard {
/**
* Initializes key event listeners
*/
initKeyboardListener(){
initKeyboardListener() {
// Adding button style on keydown
document.addEventListener("keydown", (event) => {
if(this.simpleKeyboardInstance.options.physicalKeyboardHighlight){
document.addEventListener("keydown", event => {
if (this.simpleKeyboardInstance.options.physicalKeyboardHighlight) {
let buttonPressed = this.getSimpleKeyboardLayoutKey(event);
this.simpleKeyboardInstance.dispatch(instance => {
let buttonDOM = instance.getButtonElement(buttonPressed) || instance.getButtonElement(`{${buttonPressed}}`);
let buttonDOM =
instance.getButtonElement(buttonPressed) ||
instance.getButtonElement(`{${buttonPressed}}`);
if(buttonDOM){
buttonDOM.style.backgroundColor = this.simpleKeyboardInstance.options.physicalKeyboardHighlightBgColor || "#9ab4d0";
buttonDOM.style.color = this.simpleKeyboardInstance.options.physicalKeyboardHighlightTextColor || "white";
if (buttonDOM) {
buttonDOM.style.backgroundColor =
this.simpleKeyboardInstance.options
.physicalKeyboardHighlightBgColor || "#9ab4d0";
buttonDOM.style.color =
this.simpleKeyboardInstance.options
.physicalKeyboardHighlightTextColor || "white";
}
});
}
});
// Removing button style on keyup
document.addEventListener("keyup", (event) => {
if(this.simpleKeyboardInstance.options.physicalKeyboardHighlight){
document.addEventListener("keyup", event => {
if (this.simpleKeyboardInstance.options.physicalKeyboardHighlight) {
let buttonPressed = this.getSimpleKeyboardLayoutKey(event);
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");
}
});
@@ -63,10 +73,10 @@ class PhysicalKeyboard {
* Transforms a KeyboardEvent's "key.code" string into a simple-keyboard layout format
* @param {object} event The KeyboardEvent
*/
getSimpleKeyboardLayoutKey(event){
getSimpleKeyboardLayoutKey(event) {
let output;
if(
if (
event.code.includes("Numpad") ||
event.code.includes("Shift") ||
event.code.includes("Space") ||
@@ -74,7 +84,7 @@ class PhysicalKeyboard {
event.code.includes("Control") ||
event.code.includes("Alt") ||
event.code.includes("Meta")
){
) {
output = event.code;
} else {
output = event.key;
@@ -85,7 +95,9 @@ class PhysicalKeyboard {
*/
if (
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();
}
@@ -94,4 +106,4 @@ class PhysicalKeyboard {
}
}
export default PhysicalKeyboard;
export default PhysicalKeyboard;

View File

@@ -5,7 +5,7 @@ class Utilities {
/**
* Creates an instance of the Utility service
*/
constructor(simpleKeyboardInstance){
constructor(simpleKeyboardInstance) {
/**
* @type {object} A simple-keyboard instance
*/
@@ -14,27 +14,24 @@ class Utilities {
/**
* Bindings
*/
this.getButtonClass = this.getButtonClass.bind(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);
Utilities.bindMethods(Utilities, this);
}
/**
* Adds default classes to a given button
*
*
* @param {string} button The button's layout name
* @return {string} The classes to be added to the button
*/
getButtonClass(button){
let buttonTypeClass = (button.includes("{") && button.includes("}") && button !== '{//}') ? "functionBtn" : "standardBtn";
getButtonClass(button) {
let buttonTypeClass =
button.includes("{") && button.includes("}") && button !== "{//}"
? "functionBtn"
: "standardBtn";
let buttonWithoutBraces = button.replace("{", "").replace("}", "");
let buttonNormalized = '';
let buttonNormalized = "";
if(buttonTypeClass !== "standardBtn")
if (buttonTypeClass !== "standardBtn")
buttonNormalized = ` hg-button-${buttonWithoutBraces}`;
return `hg-${buttonTypeClass}${buttonNormalized}`;
@@ -43,22 +40,22 @@ class Utilities {
/**
* Default button display labels
*/
getDefaultDiplay(){
getDefaultDiplay() {
return {
'{bksp}': 'backspace',
'{backspace}': 'backspace',
'{enter}': '< enter',
'{shift}': 'shift',
'{shiftleft}': 'shift',
'{shiftright}': 'shift',
'{alt}': 'alt',
'{s}': 'shift',
'{tab}': 'tab',
'{lock}': 'caps',
'{capslock}': 'caps',
'{accept}': 'Submit',
'{space}': ' ',
'{//}': ' ',
"{bksp}": "backspace",
"{backspace}": "backspace",
"{enter}": "< enter",
"{shift}": "shift",
"{shiftleft}": "shift",
"{shiftright}": "shift",
"{alt}": "alt",
"{s}": "shift",
"{tab}": "tab",
"{lock}": "caps",
"{capslock}": "caps",
"{accept}": "Submit",
"{space}": " ",
"{//}": " ",
"{esc}": "esc",
"{escape}": "esc",
"{f1}": "f1",
@@ -73,8 +70,8 @@ class Utilities {
"{f10}": "f10",
"{f11}": "f11",
"{f12}": "f12",
'{numpaddivide}': '/',
'{numlock}': 'lock',
"{numpaddivide}": "/",
"{numlock}": "lock",
"{arrowup}": "↑",
"{arrowleft}": "←",
"{arrowdown}": "↓",
@@ -103,18 +100,18 @@ class Utilities {
"{numpad6}": "6",
"{numpad7}": "7",
"{numpad8}": "8",
"{numpad9}": "9",
"{numpad9}": "9"
};
}
/**
* Returns the display (label) name for a given button
*
*
* @param {string} button The button's layout name
* @param {object} display The provided display option
* @param {boolean} mergeDisplay Whether the provided param value should be merged with the default one.
*/
getButtonDisplayName(button, display, mergeDisplay){
if(mergeDisplay){
getButtonDisplayName(button, display, mergeDisplay) {
if (mergeDisplay) {
display = Object.assign({}, this.getDefaultDiplay(), display);
} else {
display = display || this.getDefaultDiplay();
@@ -123,97 +120,136 @@ class Utilities {
return display[button] || button;
}
/**
* Returns the updated input resulting from clicking a given button
*
*
* @param {string} button The button's layout name
* @param {string} input The input string
* @param {object} options The simple-keyboard options object
* @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;
if((button === "{bksp}" || button === "{backspace}") && output.length > 0){
output = this.removeAt(output, caretPos);
} else if(button === "{space}")
output = this.addStringAt(output, " ", caretPos);
else if(button === "{tab}" && !(typeof options.tabCharOnTab === "boolean" && options.tabCharOnTab === false)){
output = this.addStringAt(output, "\t", caretPos);
} else if((button === "{enter}" || button === "{numpadenter}") && options.newLineOnEnter)
output = this.addStringAt(output, "\n", caretPos);
else if(button.includes("numpad") && Number.isInteger(Number(button[button.length - 2]))){
output = this.addStringAt(output, button[button.length - 2], caretPos);
}
else if(button === "{numpaddivide}")
output = this.addStringAt(output, '/', caretPos);
else if(button === "{numpadmultiply}")
output = this.addStringAt(output, '*', caretPos);
else if(button === "{numpadsubtract}")
output = this.addStringAt(output, '-', caretPos);
else if(button === "{numpadadd}")
output = this.addStringAt(output, '+', caretPos);
else if(button === "{numpaddecimal}")
output = this.addStringAt(output, '.', caretPos);
else if(button === "{" || button === "}")
output = this.addStringAt(output, button, caretPos);
else if(!button.includes("{") && !button.includes("}"))
output = this.addStringAt(output, button, caretPos);
if (
(button === "{bksp}" || button === "{backspace}") &&
output.length > 0
) {
output = this.removeAt(output, caretPos, moveCaret);
} else if (button === "{space}")
output = this.addStringAt(output, " ", caretPos, moveCaret);
else if (
button === "{tab}" &&
!(
typeof options.tabCharOnTab === "boolean" &&
options.tabCharOnTab === false
)
) {
output = this.addStringAt(output, "\t", caretPos, moveCaret);
} else if (
(button === "{enter}" || button === "{numpadenter}") &&
options.newLineOnEnter
)
output = this.addStringAt(output, "\n", caretPos, moveCaret);
else if (
button.includes("numpad") &&
Number.isInteger(Number(button[button.length - 2]))
) {
output = this.addStringAt(
output,
button[button.length - 2],
caretPos,
moveCaret
);
} else if (button === "{numpaddivide}")
output = this.addStringAt(output, "/", caretPos, moveCaret);
else if (button === "{numpadmultiply}")
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;
}
/**
* Moves the cursor position by a given amount
*
*
* @param {number} length Represents by how many characters the input should be moved
* @param {boolean} minus Whether the cursor should be moved to the left or not.
*/
updateCaretPos(length, minus){
if(minus){
if(this.simpleKeyboardInstance.caretPosition > 0)
this.simpleKeyboardInstance.caretPosition = this.simpleKeyboardInstance.caretPosition - length
} else {
this.simpleKeyboardInstance.caretPosition = this.simpleKeyboardInstance.caretPosition + length;
updateCaretPos(length, minus) {
let newCaretPos = this.updateCaretPosAction(
this.simpleKeyboardInstance,
length,
minus
);
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
*
*
* @param {string} source The source input
* @param {string} string The string to add
* @param {number} position The (cursor) position where the string should be added
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/
addStringAt(source, string, position){
addStringAt(source, string, position, moveCaret) {
let output;
if(this.simpleKeyboardInstance.options.debug){
console.log("Caret at:", position);
}
if(!position && position !== 0){
if (!position && position !== 0) {
output = source + string;
} 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
*/
if(!this.isMaxLengthReached()){
this.updateCaretPos(string.length);
if (!this.isMaxLengthReached()) {
if (moveCaret) this.updateCaretPos(string.length);
}
}
return output;
@@ -221,12 +257,13 @@ class Utilities {
/**
* Removes an amount of characters at a given position
*
*
* @param {string} source The source input
* @param {number} position The (cursor) position from where the characters should be removed
* @param {boolean} moveCaret Whether to update simple-keyboard's cursor
*/
removeAt(source, position){
if(this.simpleKeyboardInstance.caretPosition === 0){
removeAt(source, position, moveCaret) {
if (this.simpleKeyboardInstance.caretPosition === 0) {
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.
* For more info: https://mathiasbynens.be/notes/javascript-unicode
*/
if(position && position >= 0){
prevTwoChars = source.substring(position - 2, position)
if (position && position >= 0) {
prevTwoChars = source.substring(position - 2, position);
emojiMatched = prevTwoChars.match(emojiMatchedReg);
if(emojiMatched){
output = source.substr(0, (position - 2)) + source.substr(position);
this.updateCaretPos(2, true);
if (emojiMatched) {
output = source.substr(0, position - 2) + source.substr(position);
if (moveCaret) this.updateCaretPos(2, true);
} else {
output = source.substr(0, (position - 1)) + source.substr(position);
this.updateCaretPos(1, true);
output = source.substr(0, position - 1) + source.substr(position);
if (moveCaret) this.updateCaretPos(1, true);
}
} else {
prevTwoChars = source.slice(-2);
emojiMatched = prevTwoChars.match(emojiMatchedReg);
if(emojiMatched){
if (emojiMatched) {
output = source.slice(0, -2);
this.updateCaretPos(2, true);
if (moveCaret) this.updateCaretPos(2, true);
} else {
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.
*
*
* @param {object} inputObj
* @param {object} options
* @param {string} updatedInput
*/
handleMaxLength(inputObj, options, updatedInput){
handleMaxLength(inputObj, options, updatedInput) {
let maxLength = options.maxLength;
let currentInput = inputObj[options.inputName];
let condition = currentInput.length === maxLength;
if(
if (
/**
* If pressing this button won't add more characters
* We exit out of this limiter function
*/
updatedInput.length <= currentInput.length
){
) {
return false;
}
if(Number.isInteger(maxLength)){
if(options.debug){
if (Number.isInteger(maxLength)) {
if (options.debug) {
console.log("maxLength (num) reached:", condition);
}
if(condition){
if (condition) {
/**
* @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];
if(options.debug){
if (options.debug) {
console.log("maxLength (obj) reached:", condition);
}
if(condition){
if (condition) {
this.maxLengthReached = true;
return true;
} else {
@@ -325,29 +361,62 @@ class Utilities {
/**
* Gets the current value of maxLengthReached
*/
isMaxLengthReached(){
isMaxLengthReached() {
return Boolean(this.maxLengthReached);
}
/**
* Determines whether a touch device is being used
*/
isTouchDevice() {
return "ontouchstart" in window || navigator.maxTouchPoints;
}
/**
* Determines whether pointer events are supported
*/
pointerEventsSupported() {
return window.PointerEvent;
}
/**
* 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
*
*
* @param {string} string The string to transform.
*/
camelCase(string){
return string.toLowerCase().trim().split(/[.\-_\s]/g).reduce((string, word) => string + word[0].toUpperCase() + word.slice(1));
};
camelCase(string) {
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
*
*
* @param {Array} array The haystack to search in
* @param {string} value The needle to search for
*/
countInArray(array, value){
countInArray(array, value) {
return array.reduce((n, x) => n + (x === value), 0);
}
}
export default Utilities;
export default Utilities;

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', () => {
testUtil.setDOM();
@@ -416,11 +428,14 @@ it('Keyboard removeAt will exit out on caretPosition:0', () => {
let keyboard = new Keyboard();
keyboard.setInput("test");
keyboard.caretPosition = 0;
keyboard.utilities.removeAt(keyboard.getInput(), 0);
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', () => {
@@ -430,27 +445,42 @@ it('Keyboard removeAt will remove multi-byte unicodes with caretPos>0', () => {
keyboard.caretPosition = 6;
let output = keyboard.utilities.removeAt("test\uD83D\uDE00", 6);
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', () => {
testUtil.setDOM();
let keyboard = new Keyboard();
let output = keyboard.utilities.removeAt("\uD83D\uDE00");
expect(output).toBeFalsy();
output = keyboard.utilities.removeAt("\uD83D\uDE00", 0, true);
expect(output).toBeFalsy();
});
it('Keyboard removeAt will remove regular strings', () => {
testUtil.setDOM();
let keyboard = new Keyboard();
let keyboard = new Keyboard({
debug: true
});
keyboard.caretPosition = 6;
let output = keyboard.utilities.removeAt("testie", 6);
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");
});