├── .babelrc
├── .eslintrc.json
├── .gitignore
├── .storybook
└── main.js
├── LICENSE
├── README.md
├── docs
├── favicon.ico
├── iframe.html
├── index.html
├── main.5da119d0a62d15786474.bundle.js
├── main.f784816d1f43c2dfc5c8.bundle.js
├── main.f784816d1f43c2dfc5c8.bundle.js.map
├── runtime~main.8bfece81a1ead32e094c.bundle.js
├── runtime~main.f784816d1f43c2dfc5c8.bundle.js
├── runtime~main.f784816d1f43c2dfc5c8.bundle.js.map
├── sb_dll
│ ├── storybook_ui-manifest.json
│ ├── storybook_ui_dll.LICENCE
│ └── storybook_ui_dll.js
├── vendors~main.e8135d03a47b34cf111e.bundle.js
├── vendors~main.f784816d1f43c2dfc5c8.bundle.js
├── vendors~main.f784816d1f43c2dfc5c8.bundle.js.LICENSE.txt
└── vendors~main.f784816d1f43c2dfc5c8.bundle.js.map
├── examples
└── example.js
├── gulpfile.js
├── lib
└── index.js
├── package-lock.json
├── package.json
├── src
└── index.js
└── story.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env",
4 | "@babel/preset-react"
5 | ],
6 | "plugins": [
7 | "@babel/plugin-syntax-dynamic-import",
8 | "@babel/plugin-syntax-import-meta",
9 | "@babel/plugin-proposal-class-properties",
10 | "@babel/plugin-proposal-json-strings",
11 | [
12 | "@babel/plugin-proposal-decorators",
13 | {
14 | "legacy": true
15 | }
16 | ],
17 | "@babel/plugin-proposal-function-sent",
18 | "@babel/plugin-proposal-export-namespace-from",
19 | "@babel/plugin-proposal-numeric-separator",
20 | "@babel/plugin-proposal-throw-expressions",
21 | "@babel/plugin-proposal-export-default-from",
22 | "@babel/plugin-proposal-logical-assignment-operators",
23 | "@babel/plugin-proposal-optional-chaining",
24 | [
25 | "@babel/plugin-proposal-pipeline-operator",
26 | {
27 | "proposal": "minimal"
28 | }
29 | ],
30 | "@babel/plugin-proposal-nullish-coalescing-operator",
31 | "@babel/plugin-proposal-do-expressions",
32 | "@babel/plugin-proposal-function-bind",
33 | "@babel/plugin-transform-modules-commonjs"
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "env": {
4 | "es6": true,
5 | "node": true,
6 | "browser": true
7 | },
8 | "extends": "eslint:recommended",
9 | "parserOptions": {
10 | "ecmaVersion": 7,
11 | "sourceType": "module",
12 | "ecmaFeatures": {
13 | "jsx": true,
14 | "experimentalObjectRestSpread": true,
15 | "classes": true
16 | }
17 | },
18 | "plugins": [
19 | "react"
20 | ],
21 | "rules": {
22 | "indent": [
23 | "error",
24 | 2
25 | ],
26 | "quotes": [
27 | "error",
28 | "single"
29 | ],
30 | "linebreak-style": [
31 | "error",
32 | "unix"
33 | ],
34 | "semi": [
35 | "error",
36 | "never"
37 | ],
38 | "comma-dangle": [
39 | "warn",
40 | "always-multiline"
41 | ],
42 | "constructor-super": "error",
43 | "no-confusing-arrow": "error",
44 | "no-constant-condition": "error",
45 | "no-class-assign": "error",
46 | "no-const-assign": "error",
47 | "no-dupe-class-members": "error",
48 | "no-var": "warn",
49 | "no-this-before-super": "error",
50 | "no-extra-boolean-cast": "off",
51 | "object-shorthand": [
52 | "error",
53 | "always"
54 | ],
55 | "prefer-spread": "warn",
56 | "prefer-template": "warn",
57 | "require-yield": "error",
58 | "jsx-quotes": "warn",
59 | "react/jsx-boolean-value": "warn",
60 | "react/jsx-curly-spacing": "warn",
61 | "react/jsx-no-duplicate-props": "warn",
62 | "react/jsx-no-undef": "warn",
63 | "react/jsx-uses-react": "warn",
64 | "react/jsx-uses-vars": "warn",
65 | "react/no-danger": "warn",
66 | "react/no-did-mount-set-state": "warn",
67 | "react/no-did-update-set-state": "warn",
68 | "react/no-direct-mutation-state": "warn",
69 | "react/no-multi-comp": "warn",
70 | "react/no-set-state": "warn",
71 | "react/no-unknown-property": "warn",
72 | "react/prefer-es6-class": "warn",
73 | "react/prop-types": "warn",
74 | "react/react-in-jsx-scope": "warn",
75 | "react/self-closing-comp": "warn",
76 | "react/sort-comp": "warn"
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
18 | .grunt
19 |
20 | # node-waf configuration
21 | .lock-wscript
22 |
23 | # Compiled binary addons (http://nodejs.org/api/addons.html)
24 | build/Release
25 |
26 | # Dependency directory
27 | node_modules
28 |
29 | # Optional npm cache directory
30 | .npm
31 |
32 | # Optional REPL history
33 | .node_repl_history
34 |
35 |
36 | lib/worker.js
--------------------------------------------------------------------------------
/.storybook/main.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | stories: ['../story.js'],
3 | };
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2017 Thomas Billiet
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://badge.fury.io/js/react-barcode-reader)
2 |
3 | ## Introduction
4 | A [React](https://facebook.github.io/react/) component for reading barcode and QR codes from devices that are represent as keyboard to the system.
5 |
6 | ## Demo
7 | [kybarg.github.io/react-barcode-reader/](https://kybarg.github.io/react-barcode-reader/)
8 |
9 | ## Install
10 | `npm install --save react-barcode-reader`
11 |
12 | ## Example
13 |
14 | ```js
15 | import React, { Component } from 'react'
16 | import BarcodeReader from 'react-barcode-reader'
17 |
18 | class Test extends Component {
19 | constructor(props){
20 | super(props)
21 | this.state = {
22 | result: 'No result',
23 | }
24 |
25 | this.handleScan = this.handleScan.bind(this)
26 | }
27 | handleScan(data){
28 | this.setState({
29 | result: data,
30 | })
31 | }
32 | handleError(err){
33 | console.error(err)
34 | }
35 | render(){
36 |
37 | return(
38 |
39 |
43 |
{this.state.result}
44 |
45 | )
46 | }
47 | }
48 | ```
49 |
50 | ## Props
51 | | Prop | Type | Default Value | Description |
52 | |---|---|---|---|
53 | | onScan | func | | Callback after detection of a successfull scanning (scanned string in parameter) |
54 | | onError | func | | Callback after detection of a unsuccessfull scanning (scanned string in parameter) |
55 | | HonReceive | func | | Callback after receiving and processing a char (scanned char in parameter)
56 | | onKeyDetect | func | | Callback after detecting a keyDown (key char in parameter) - in contrast to onReceive, this fires for non-character keys like tab, arrows, etc. too!
57 | | timeBeforeScanTest | number | 100 | Wait duration (ms) after keypress event to check if scanning is finished
58 | | avgTimeByChar | number | 30 | Average time (ms) between 2 chars. Used to do difference between keyboard typing and scanning
59 | | minLength | number | 6 | Minimum length for a scanning
60 | | endChar | [number] | [9, 13] | Chars to remove and means end of scanning
61 | | startChar | [number] | [] | Chars to remove and means start of scanning
62 | | scanButtonKeyCode | number | | Key code of the scanner hardware button (if the scanner button a acts as a key itself)
63 | | scanButtonLongPressThreshold | number | 3 | How many times the hardware button should issue a pressed event before a barcode is read to detect a longpress
64 | | onScanButtonLongPressed | func | | Callback after detection of a successfull scan while the scan button was pressed and held down
65 | | stopPropagation | bool | false | Stop immediate propagation on keypress event
66 | | preventDefault | bool | false | Prevent default action on keypress event
67 | | testCode | string | | Test string for simulating
68 |
69 | ## Dev
70 |
71 | ### Install dependencies
72 | `npm install`
73 |
74 | ### Build
75 | `npm run build`
76 |
77 | ### Demo
78 | `npm run storybook`
79 |
80 | ### Test
81 | `npm test`
82 |
83 | ### Linting
84 | `npm run lint`
85 |
86 | ## License
87 | The MIT License (MIT)
88 |
89 | Copyright (c) 2017 Thomas Billiet
90 |
91 | Permission is hereby granted, free of charge, to any person obtaining a copy
92 | of this software and associated documentation files (the "Software"), to deal
93 | in the Software without restriction, including without limitation the rights
94 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
95 | copies of the Software, and to permit persons to whom the Software is
96 | furnished to do so, subject to the following conditions:
97 |
98 | The above copyright notice and this permission notice shall be included in all
99 | copies or substantial portions of the Software.
100 |
101 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
102 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
103 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
104 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
105 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
106 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
107 | SOFTWARE.
108 |
--------------------------------------------------------------------------------
/docs/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kybarg/react-barcode-reader/3e1b1c2e635dc4d63dd5c19fb5858b4dcb17d0d6/docs/favicon.ico
--------------------------------------------------------------------------------
/docs/iframe.html:
--------------------------------------------------------------------------------
1 | StorybookNo Preview
Sorry, but you either have no stories or none are selected somehow.
- Please check the Storybook config.
- Try reloading the page.
If the problem persists, check the browser console, or the terminal you've run Storybook from.
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 | Storybook
--------------------------------------------------------------------------------
/docs/main.5da119d0a62d15786474.bundle.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[0],{401:function(n,o,p){p(402),n.exports=p(545)},464:function(n,o){}},[[401,1,2]]]);
--------------------------------------------------------------------------------
/docs/main.f784816d1f43c2dfc5c8.bundle.js:
--------------------------------------------------------------------------------
1 | (window.webpackJsonp=window.webpackJsonp||[]).push([[0],{270:function(module,exports,__webpack_require__){__webpack_require__(271),__webpack_require__(417),module.exports=__webpack_require__(418)},335:function(module,exports){},418:function(module,exports,__webpack_require__){"use strict";(function(module){var _react=__webpack_require__(214);module._StorybookPreserveDecorators=!0,(0,_react.configure)([__webpack_require__(606)],module)}).call(this,__webpack_require__(57)(module))},606:function(module,exports,__webpack_require__){var map={"./story.js":607};function webpackContext(req){var id=webpackContextResolve(req);return __webpack_require__(id)}function webpackContextResolve(req){if(!__webpack_require__.o(map,req)){var e=new Error("Cannot find module '"+req+"'");throw e.code="MODULE_NOT_FOUND",e}return map[req]}webpackContext.keys=function webpackContextKeys(){return Object.keys(map)},webpackContext.resolve=webpackContextResolve,module.exports=webpackContext,webpackContext.id=606},607:function(module,exports,__webpack_require__){"use strict";(function(module){__webpack_require__(16),__webpack_require__(18),__webpack_require__(19),__webpack_require__(17),__webpack_require__(34),__webpack_require__(112),__webpack_require__(52),__webpack_require__(81),__webpack_require__(113),__webpack_require__(14),__webpack_require__(267),__webpack_require__(35),__webpack_require__(20),__webpack_require__(21);var _react=function _interopRequireWildcard(obj){if(obj&&obj.__esModule)return obj;if(null===obj||"object"!==_typeof(obj)&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache();if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if(Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(__webpack_require__(66)),_react2=__webpack_require__(214),_addonActions=__webpack_require__(608),_lib=function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}(__webpack_require__(621));function _getRequireWildcardCache(){if("function"!=typeof WeakMap)return null;var cache=new WeakMap;return _getRequireWildcardCache=function(){return cache},cache}function _typeof(obj){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(obj){return typeof obj}:function(obj){return obj&&"function"==typeof Symbol&&obj.constructor===Symbol&&obj!==Symbol.prototype?"symbol":typeof obj})(obj)}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor))throw new TypeError("Cannot call a class as a function")}function _defineProperties(target,props){for(var descriptor,i=0;i=minLength&&_this.lastCharTime-_this.firstCharTime<_this.stringWriting.length*avgTimeByChar)return onScanButtonLongPressed&&_this.scanButtonCounter>scanButtonLongPressThreshold?onScanButtonLongPressed(_this.stringWriting,_this.scanButtonCounter):onScan&&onScan(_this.stringWriting,_this.scanButtonCounter),_this.initScannerDetection(),!0;var errorMsg="";return _this.stringWriting.length_this.stringWriting.length*avgTimeByChar&&(errorMsg="Average key character time should be less or equal ".concat(avgTimeByChar,"ms")),onError&&onError(_this.stringWriting,errorMsg),_this.initScannerDetection(),!1})),_defineProperty(_assertThisInitialized(_this),"handleKeyPress",(function(e){var _this$props2=_this.props,onKeyDetect=_this$props2.onKeyDetect,onReceive=_this$props2.onReceive,scanButtonKeyCode=_this$props2.scanButtonKeyCode,stopPropagation=_this$props2.stopPropagation,preventDefault=_this$props2.preventDefault,endChar=_this$props2.endChar,startChar=_this$props2.startChar,timeBeforeScanTest=_this$props2.timeBeforeScanTest,target=e.target;target instanceof window.HTMLElement&&function isInput(element){if(!element)return!1;var tagName=element.tagName,editable=function isContentEditable(element){return"function"==typeof element.getAttribute&&!!element.getAttribute("contenteditable")}(element);return"INPUT"===tagName||"TEXTAREA"===tagName||editable}(target)||(scanButtonKeyCode&&e.which===scanButtonKeyCode&&(_this.scanButtonCounter+=1,e.preventDefault(),e.stopImmediatePropagation()),onKeyDetect&&onKeyDetect(e),stopPropagation&&e.stopImmediatePropagation(),preventDefault&&e.preventDefault(),_this.firstCharTime&&-1!==endChar.indexOf(e.which)?(e.preventDefault(),e.stopImmediatePropagation(),_this.callIsScanner=!0):_this.firstCharTime||-1===startChar.indexOf(e.which)?(void 0!==e.which&&(_this.stringWriting+=String.fromCharCode(e.which)),_this.callIsScanner=!1):(e.preventDefault(),e.stopImmediatePropagation(),_this.callIsScanner=!1),_this.firstCharTime||(_this.firstCharTime=Date.now()),_this.lastCharTime=Date.now(),_this.testTimer&&clearTimeout(_this.testTimer),_this.callIsScanner?(_this.scannerDetectionTest(),_this.testTimer=!1):_this.testTimer=setTimeout(_this.scannerDetectionTest,timeBeforeScanTest),onReceive&&onReceive(e))})),_this.firstCharTime=0,_this.lastCharTime=0,_this.stringWriting="",_this.callIsScanner=!1,_this.testTimer=!1,_this.scanButtonCounter=0,_this}return function _createClass(Constructor,protoProps,staticProps){return protoProps&&_defineProperties(Constructor.prototype,protoProps),staticProps&&_defineProperties(Constructor,staticProps),Constructor}(BarcodeScanner,[{key:"componentDidMount",value:function(){inIframe&&window.parent.document.addEventListener("keypress",this.handleKeyPress),window.document.addEventListener("keypress",this.handleKeyPress)}},{key:"componentWillUnmount",value:function(){inIframe&&window.parent.document.removeEventListener("keypress",this.handleKeyPress),window.document.removeEventListener("keypress",this.handleKeyPress)}},{key:"render",value:function(){return this.props.testCode&&this.scannerDetectionTest(this.props.testCode),null}}]),BarcodeScanner}(_react.default.Component);BarcodeScanner.propTypes={onScan:_propTypes.default.func,onError:_propTypes.default.func,onReceive:_propTypes.default.func,onKeyDetect:_propTypes.default.func,timeBeforeScanTest:_propTypes.default.number,avgTimeByChar:_propTypes.default.number,minLength:_propTypes.default.number,endChar:_propTypes.default.arrayOf(_propTypes.default.number),startChar:_propTypes.default.arrayOf(_propTypes.default.number),scanButtonKeyCode:_propTypes.default.number,scanButtonLongPressThreshold:_propTypes.default.number,onScanButtonLongPressed:_propTypes.default.func,stopPropagation:_propTypes.default.bool,preventDefault:_propTypes.default.bool,testCode:_propTypes.default.string},BarcodeScanner.defaultProps={timeBeforeScanTest:100,avgTimeByChar:30,minLength:6,endChar:[9,13],startChar:[],scanButtonLongPressThreshold:3,stopPropagation:!1,preventDefault:!1};var _default=BarcodeScanner;exports.default=_default}},[[270,1,2]]]);
2 | //# sourceMappingURL=main.f784816d1f43c2dfc5c8.bundle.js.map
--------------------------------------------------------------------------------
/docs/main.f784816d1f43c2dfc5c8.bundle.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"main.f784816d1f43c2dfc5c8.bundle.js","sources":["webpack:///main.f784816d1f43c2dfc5c8.bundle.js"],"mappings":"AAAA","sourceRoot":""}
--------------------------------------------------------------------------------
/docs/runtime~main.8bfece81a1ead32e094c.bundle.js:
--------------------------------------------------------------------------------
1 | !function(e){function r(r){for(var n,l,f=r[0],i=r[1],a=r[2],c=0,s=[];c
40 | *
41 | * Copyright (c) 2014-2017, Jon Schlinkert.
42 | * Released under the MIT License.
43 | */
44 |
45 | /**
46 | * @license
47 | * Lodash
48 | * Copyright OpenJS Foundation and other contributors
49 | * Released under MIT license
50 | * Based on Underscore.js 1.8.3
51 | * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
52 | */
53 |
54 | /** @license React v0.18.0
55 | * scheduler.production.min.js
56 | *
57 | * Copyright (c) Facebook, Inc. and its affiliates.
58 | *
59 | * This source code is licensed under the MIT license found in the
60 | * LICENSE file in the root directory of this source tree.
61 | */
62 |
63 | /** @license React v16.12.0
64 | * react-dom.production.min.js
65 | *
66 | * Copyright (c) Facebook, Inc. and its affiliates.
67 | *
68 | * This source code is licensed under the MIT license found in the
69 | * LICENSE file in the root directory of this source tree.
70 | */
71 |
72 | /** @license React v16.12.0
73 | * react-is.production.min.js
74 | *
75 | * Copyright (c) Facebook, Inc. and its affiliates.
76 | *
77 | * This source code is licensed under the MIT license found in the
78 | * LICENSE file in the root directory of this source tree.
79 | */
80 |
81 | /** @license React v16.12.0
82 | * react.production.min.js
83 | *
84 | * Copyright (c) Facebook, Inc. and its affiliates.
85 | *
86 | * This source code is licensed under the MIT license found in the
87 | * LICENSE file in the root directory of this source tree.
88 | */
89 |
90 | /**!
91 | * @fileOverview Kickass library to create and place poppers near their reference elements.
92 | * @version 1.16.1
93 | * @license
94 | * Copyright (c) 2016 Federico Zivolo and contributors
95 | *
96 | * Permission is hereby granted, free of charge, to any person obtaining a copy
97 | * of this software and associated documentation files (the "Software"), to deal
98 | * in the Software without restriction, including without limitation the rights
99 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
100 | * copies of the Software, and to permit persons to whom the Software is
101 | * furnished to do so, subject to the following conditions:
102 | *
103 | * The above copyright notice and this permission notice shall be included in all
104 | * copies or substantial portions of the Software.
105 | *
106 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
107 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
108 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
109 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
110 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
111 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
112 | * SOFTWARE.
113 | */
114 |
--------------------------------------------------------------------------------
/docs/vendors~main.f784816d1f43c2dfc5c8.bundle.js.LICENSE.txt:
--------------------------------------------------------------------------------
1 | /*
2 | object-assign
3 | (c) Sindre Sorhus
4 | @license MIT
5 | */
6 |
7 | /*!
8 | * https://github.com/es-shims/es5-shim
9 | * @license es5-shim Copyright 2009-2020 by contributors, MIT License
10 | * see https://github.com/es-shims/es5-shim/blob/master/LICENSE
11 | */
12 |
13 | /*!
14 | * https://github.com/paulmillr/es6-shim
15 | * @license es6-shim Copyright 2013-2016 by Paul Miller (http://paulmillr.com)
16 | * and contributors, MIT License
17 | * es6-shim: v0.35.4
18 | * see https://github.com/paulmillr/es6-shim/blob/0.35.3/LICENSE
19 | * Details and documentation:
20 | * https://github.com/paulmillr/es6-shim/
21 | */
22 |
23 | /*!
24 | * is-plain-object
25 | *
26 | * Copyright (c) 2014-2017, Jon Schlinkert.
27 | * Released under the MIT License.
28 | */
29 |
30 | /*!
31 | * isobject
32 | *
33 | * Copyright (c) 2014-2017, Jon Schlinkert.
34 | * Released under the MIT License.
35 | */
36 |
37 | /** @license React v0.19.1
38 | * scheduler.production.min.js
39 | *
40 | * Copyright (c) Facebook, Inc. and its affiliates.
41 | *
42 | * This source code is licensed under the MIT license found in the
43 | * LICENSE file in the root directory of this source tree.
44 | */
45 |
46 | /** @license React v16.13.1
47 | * react-dom.production.min.js
48 | *
49 | * Copyright (c) Facebook, Inc. and its affiliates.
50 | *
51 | * This source code is licensed under the MIT license found in the
52 | * LICENSE file in the root directory of this source tree.
53 | */
54 |
55 | /** @license React v16.13.1
56 | * react.production.min.js
57 | *
58 | * Copyright (c) Facebook, Inc. and its affiliates.
59 | *
60 | * This source code is licensed under the MIT license found in the
61 | * LICENSE file in the root directory of this source tree.
62 | */
63 |
--------------------------------------------------------------------------------
/docs/vendors~main.f784816d1f43c2dfc5c8.bundle.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"vendors~main.f784816d1f43c2dfc5c8.bundle.js","sources":["webpack:///vendors~main.f784816d1f43c2dfc5c8.bundle.js"],"mappings":";AAAA","sourceRoot":""}
--------------------------------------------------------------------------------
/examples/example.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import BarcodeReader from 'react-barcode-reader'
3 |
4 | class Example extends Component {
5 | constructor(props){
6 | super(props)
7 | this.state = {
8 | result: 'No result',
9 | }
10 |
11 | this.handleScan = this.handleScan.bind(this)
12 | }
13 | handleScan(result){
14 | if(result){
15 | this.setState({ result })
16 | }
17 | }
18 | handleError(err){
19 | console.error(err)
20 | }
21 | render(){
22 | return(
23 |
24 |
28 |
{this.state.result}
29 |
30 | )
31 | }
32 | }
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp')
2 | const fs = require('fs')
3 | const del = require('del')
4 | const inlineStr = require('gulp-inline-str')
5 | const babel = require('gulp-babel')
6 |
7 | const babelOptions = JSON.parse(fs.readFileSync('./.babelrc', 'utf8'))
8 |
9 | const paths = {
10 | scripts: [ 'src/index.js'],
11 | destination: './lib',
12 | }
13 |
14 | gulp.task('clean', function() {
15 | return del([ paths.destination + '/*.js' ])
16 | })
17 |
18 | gulp.task('build', function() {
19 | return gulp
20 | .src(paths.scripts)
21 | .pipe(inlineStr({ basePath: paths.destination }))
22 | .pipe(babel(babelOptions))
23 | .pipe(gulp.dest(paths.destination))
24 | })
25 |
26 | // Rerun the task when a file changes
27 | gulp.task('watch', function() {
28 | gulp.watch(paths.scripts, gulp.series([ 'build' ]))
29 | })
30 |
31 | // The default task (called when you run `gulp` from cli)
32 | gulp.task('default', gulp.series([ 'build' ]))
33 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | exports["default"] = void 0;
7 |
8 | var _react = _interopRequireDefault(require("react"));
9 |
10 | var _propTypes = _interopRequireDefault(require("prop-types"));
11 |
12 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
13 |
14 | function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
15 |
16 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
17 |
18 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
19 |
20 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
21 |
22 | function _createSuper(Derived) { return function () { var Super = _getPrototypeOf(Derived), result; if (_isNativeReflectConstruct()) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
23 |
24 | function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
25 |
26 | function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
27 |
28 | function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
29 |
30 | function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
31 |
32 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
33 |
34 | function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
35 |
36 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
37 |
38 | function isContentEditable(element) {
39 | if (typeof element.getAttribute !== 'function') {
40 | return false;
41 | }
42 |
43 | return !!element.getAttribute('contenteditable');
44 | }
45 |
46 | function isInput(element) {
47 | if (!element) {
48 | return false;
49 | }
50 |
51 | var tagName = element.tagName;
52 | var editable = isContentEditable(element);
53 | return tagName === 'INPUT' || tagName === 'TEXTAREA' || editable;
54 | }
55 |
56 | function inIframe() {
57 | try {
58 | return window.self !== window.top;
59 | } catch (e) {
60 | return true;
61 | }
62 | }
63 |
64 | var BarcodeScanner = /*#__PURE__*/function (_React$Component) {
65 | _inherits(BarcodeScanner, _React$Component);
66 |
67 | var _super = _createSuper(BarcodeScanner);
68 |
69 | function BarcodeScanner(props) {
70 | var _this;
71 |
72 | _classCallCheck(this, BarcodeScanner);
73 |
74 | _this = _super.call(this, props);
75 |
76 | _defineProperty(_assertThisInitialized(_this), "initScannerDetection", function () {
77 | _this.firstCharTime = 0;
78 | _this.stringWriting = '';
79 | _this.scanButtonCounter = 0;
80 | });
81 |
82 | _defineProperty(_assertThisInitialized(_this), "scannerDetectionTest", function (s) {
83 | var _this$props = _this.props,
84 | minLength = _this$props.minLength,
85 | avgTimeByChar = _this$props.avgTimeByChar,
86 | onScanButtonLongPressed = _this$props.onScanButtonLongPressed,
87 | scanButtonLongPressThreshold = _this$props.scanButtonLongPressThreshold,
88 | onScan = _this$props.onScan,
89 | onError = _this$props.onError; // If string is given, test it
90 |
91 | if (s) {
92 | _this.firstCharTime = 0;
93 | _this.lastCharTime = 0;
94 | _this.stringWriting = s;
95 | }
96 |
97 | if (!_this.scanButtonCounter) {
98 | _this.scanButtonCounter = 1;
99 | } // If all condition are good (length, time...), call the callback and re-initialize the plugin for next scanning
100 | // Else, just re-initialize
101 |
102 |
103 | if (_this.stringWriting.length >= minLength && _this.lastCharTime - _this.firstCharTime < _this.stringWriting.length * avgTimeByChar) {
104 | if (onScanButtonLongPressed && _this.scanButtonCounter > scanButtonLongPressThreshold) onScanButtonLongPressed(_this.stringWriting, _this.scanButtonCounter);else if (onScan) onScan(_this.stringWriting, _this.scanButtonCounter);
105 |
106 | _this.initScannerDetection();
107 |
108 | return true;
109 | }
110 |
111 | var errorMsg = '';
112 |
113 | if (_this.stringWriting.length < minLength) {
114 | errorMsg = "String length should be greater or equal ".concat(minLength);
115 | } else {
116 | if (_this.lastCharTime - _this.firstCharTime > _this.stringWriting.length * avgTimeByChar) {
117 | errorMsg = "Average key character time should be less or equal ".concat(avgTimeByChar, "ms");
118 | }
119 | }
120 |
121 | if (onError) onError(_this.stringWriting, errorMsg);
122 |
123 | _this.initScannerDetection();
124 |
125 | return false;
126 | });
127 |
128 | _defineProperty(_assertThisInitialized(_this), "handleKeyPress", function (e) {
129 | var _this$props2 = _this.props,
130 | onKeyDetect = _this$props2.onKeyDetect,
131 | onReceive = _this$props2.onReceive,
132 | scanButtonKeyCode = _this$props2.scanButtonKeyCode,
133 | stopPropagation = _this$props2.stopPropagation,
134 | preventDefault = _this$props2.preventDefault,
135 | endChar = _this$props2.endChar,
136 | startChar = _this$props2.startChar,
137 | timeBeforeScanTest = _this$props2.timeBeforeScanTest;
138 | var target = e.target;
139 |
140 | if (target instanceof window.HTMLElement && isInput(target)) {
141 | return;
142 | } // If it's just the button of the scanner, ignore it and wait for the real input
143 |
144 |
145 | if (scanButtonKeyCode && e.which === scanButtonKeyCode) {
146 | _this.scanButtonCounter += 1; // Cancel default
147 |
148 | e.preventDefault();
149 | e.stopImmediatePropagation();
150 | } // Fire keyDetect event in any case!
151 |
152 |
153 | if (onKeyDetect) onKeyDetect(e);
154 | if (stopPropagation) e.stopImmediatePropagation();
155 | if (preventDefault) e.preventDefault();
156 |
157 | if (_this.firstCharTime && endChar.indexOf(e.which) !== -1) {
158 | e.preventDefault();
159 | e.stopImmediatePropagation();
160 | _this.callIsScanner = true;
161 | } else if (!_this.firstCharTime && startChar.indexOf(e.which) !== -1) {
162 | e.preventDefault();
163 | e.stopImmediatePropagation();
164 | _this.callIsScanner = false;
165 | } else {
166 | if (typeof e.which !== 'undefined') {
167 | _this.stringWriting += String.fromCharCode(e.which);
168 | }
169 |
170 | _this.callIsScanner = false;
171 | }
172 |
173 | if (!_this.firstCharTime) {
174 | _this.firstCharTime = Date.now();
175 | }
176 |
177 | _this.lastCharTime = Date.now();
178 | if (_this.testTimer) clearTimeout(_this.testTimer);
179 |
180 | if (_this.callIsScanner) {
181 | _this.scannerDetectionTest();
182 |
183 | _this.testTimer = false;
184 | } else {
185 | _this.testTimer = setTimeout(_this.scannerDetectionTest, timeBeforeScanTest);
186 | }
187 |
188 | if (onReceive) onReceive(e);
189 | });
190 |
191 | _this.firstCharTime = 0;
192 | _this.lastCharTime = 0;
193 | _this.stringWriting = '';
194 | _this.callIsScanner = false;
195 | _this.testTimer = false;
196 | _this.scanButtonCounter = 0;
197 | return _this;
198 | }
199 |
200 | _createClass(BarcodeScanner, [{
201 | key: "componentDidMount",
202 | value: function componentDidMount() {
203 | if (inIframe) window.parent.document.addEventListener('keypress', this.handleKeyPress);
204 | window.document.addEventListener('keypress', this.handleKeyPress);
205 | }
206 | }, {
207 | key: "componentWillUnmount",
208 | value: function componentWillUnmount() {
209 | if (inIframe) window.parent.document.removeEventListener('keypress', this.handleKeyPress);
210 | window.document.removeEventListener('keypress', this.handleKeyPress);
211 | }
212 | }, {
213 | key: "render",
214 | value: function render() {
215 | if (this.props.testCode) this.scannerDetectionTest(this.props.testCode);
216 | return null;
217 | }
218 | }]);
219 |
220 | return BarcodeScanner;
221 | }(_react["default"].Component);
222 |
223 | BarcodeScanner.propTypes = {
224 | onScan: _propTypes["default"].func,
225 | // Callback after detection of a successfull scanning (scanned string in parameter)
226 | onError: _propTypes["default"].func,
227 | // Callback after detection of a unsuccessfull scanning (scanned string in parameter)
228 | onReceive: _propTypes["default"].func,
229 | // Callback after receiving and processing a char (scanned char in parameter)
230 | onKeyDetect: _propTypes["default"].func,
231 | // Callback after detecting a keyDown (key char in parameter) - in contrast to onReceive, this fires for non-character keys like tab, arrows, etc. too!
232 | timeBeforeScanTest: _propTypes["default"].number,
233 | // Wait duration (ms) after keypress event to check if scanning is finished
234 | avgTimeByChar: _propTypes["default"].number,
235 | // Average time (ms) between 2 chars. Used to do difference between keyboard typing and scanning
236 | minLength: _propTypes["default"].number,
237 | // Minimum length for a scanning
238 | endChar: _propTypes["default"].arrayOf(_propTypes["default"].number),
239 | // Chars to remove and means end of scanning
240 | startChar: _propTypes["default"].arrayOf(_propTypes["default"].number),
241 | // Chars to remove and means start of scanning
242 | scanButtonKeyCode: _propTypes["default"].number,
243 | // Key code of the scanner hardware button (if the scanner button a acts as a key itself)
244 | scanButtonLongPressThreshold: _propTypes["default"].number,
245 | // How many times the hardware button should issue a pressed event before a barcode is read to detect a longpress
246 | onScanButtonLongPressed: _propTypes["default"].func,
247 | // Callback after detection of a successfull scan while the scan button was pressed and held down
248 | stopPropagation: _propTypes["default"].bool,
249 | // Stop immediate propagation on keypress event
250 | preventDefault: _propTypes["default"].bool,
251 | // Prevent default action on keypress event
252 | testCode: _propTypes["default"].string // Test string for simulating
253 |
254 | };
255 | BarcodeScanner.defaultProps = {
256 | timeBeforeScanTest: 100,
257 | avgTimeByChar: 30,
258 | minLength: 6,
259 | endChar: [9, 13],
260 | startChar: [],
261 | scanButtonLongPressThreshold: 3,
262 | stopPropagation: false,
263 | preventDefault: false
264 | };
265 | var _default = BarcodeScanner;
266 | exports["default"] = _default;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-barcode-reader",
3 | "version": "0.0.2",
4 | "description": "A react component for reading barcodes and Qr codes from keybord input devices.",
5 | "main": "./lib/index.js",
6 | "exports": {
7 | ".": "./lib/index.js"
8 | },
9 | "module": "commonjs",
10 | "scripts": {
11 | "storybook": "start-storybook -p 9001",
12 | "storybook:export": "build-storybook -c .storybook -o ./docs",
13 | "build": "gulp",
14 | "build:watch": "gulp watch",
15 | "lint": "eslint -c ./.eslintrc.json ./src"
16 | },
17 | "repository": {
18 | "type": "git",
19 | "url": "https://github.com/kybarg/react-barcode-reader.git"
20 | },
21 | "keywords": [
22 | "react",
23 | "qr",
24 | "qrcode",
25 | "qrreader",
26 | "qrscanner",
27 | "barcode",
28 | "barcode-reader"
29 | ],
30 | "author": "Ruslan Kyba ",
31 | "license": "MIT",
32 | "bugs": {
33 | "url": "https://github.com/kybarg/react-barcode-reader/issues"
34 | },
35 | "homepage": "https://github.com/kybarg/react-barcode-reader#readme",
36 | "devDependencies": {
37 | "@babel/cli": "^7.8.4",
38 | "@babel/core": "^7.9.0",
39 | "@babel/plugin-proposal-class-properties": "^7.8.3",
40 | "@babel/plugin-proposal-decorators": "^7.8.3",
41 | "@babel/plugin-proposal-do-expressions": "^7.8.3",
42 | "@babel/plugin-proposal-export-default-from": "^7.8.3",
43 | "@babel/plugin-proposal-export-namespace-from": "^7.8.3",
44 | "@babel/plugin-proposal-function-bind": "^7.8.3",
45 | "@babel/plugin-proposal-function-sent": "^7.8.3",
46 | "@babel/plugin-proposal-json-strings": "^7.8.3",
47 | "@babel/plugin-proposal-logical-assignment-operators": "^7.8.3",
48 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3",
49 | "@babel/plugin-proposal-numeric-separator": "^7.8.3",
50 | "@babel/plugin-proposal-optional-chaining": "^7.9.0",
51 | "@babel/plugin-proposal-pipeline-operator": "^7.8.3",
52 | "@babel/plugin-proposal-throw-expressions": "^7.8.3",
53 | "@babel/plugin-syntax-dynamic-import": "^7.8.3",
54 | "@babel/plugin-syntax-import-meta": "^7.8.3",
55 | "@babel/plugin-transform-modules-commonjs": "^7.9.0",
56 | "@babel/preset-env": "^7.9.0",
57 | "@babel/preset-react": "^7.9.4",
58 | "@storybook/addon-actions": "^5.3.18",
59 | "@storybook/cli": "^5.3.18",
60 | "@storybook/react": "^5.3.18",
61 | "babel-core": "^7.0.0-bridge.0",
62 | "babel-eslint": "^10.1.0",
63 | "del": "^5.1.0",
64 | "eslint": "^6.8.0",
65 | "eslint-plugin-react": "^7.19.0",
66 | "gulp": "^4.0.2",
67 | "gulp-babel": "^8.0.0",
68 | "gulp-butternut": "^1.0.0",
69 | "gulp-concat": "^2.6.1",
70 | "gulp-inline-str": "^0.1.0"
71 | },
72 | "dependencies": {
73 | "babel-loader": "^8.1.0",
74 | "prop-types": "^15.7.2",
75 | "react": "^16.13.1",
76 | "react-dom": "^16.13.1"
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 |
4 | function isContentEditable(element) {
5 | if (typeof element.getAttribute !== 'function') {
6 | return false
7 | }
8 |
9 | return !!element.getAttribute('contenteditable')
10 | }
11 |
12 | function isInput(element) {
13 | if (!element) {
14 | return false
15 | }
16 |
17 | const { tagName } = element
18 | const editable = isContentEditable(element)
19 |
20 | return tagName === 'INPUT' || tagName === 'TEXTAREA' || editable
21 | }
22 |
23 | function inIframe() {
24 | try {
25 | return window.self !== window.top
26 | } catch (e) {
27 | return true
28 | }
29 | }
30 |
31 | class BarcodeScanner extends React.Component {
32 | constructor(props) {
33 | super(props)
34 |
35 | this.firstCharTime = 0
36 | this.lastCharTime = 0
37 | this.stringWriting = ''
38 | this.callIsScanner = false
39 | this.testTimer = false
40 | this.scanButtonCounter = 0
41 | }
42 |
43 | componentDidMount() {
44 | if (inIframe) window.parent.document.addEventListener('keypress', this.handleKeyPress)
45 | window.document.addEventListener('keypress', this.handleKeyPress)
46 | }
47 |
48 | componentWillUnmount() {
49 | if (inIframe) window.parent.document.removeEventListener('keypress', this.handleKeyPress)
50 | window.document.removeEventListener('keypress', this.handleKeyPress)
51 | }
52 |
53 | initScannerDetection = () => {
54 | this.firstCharTime = 0
55 | this.stringWriting = ''
56 | this.scanButtonCounter = 0
57 | }
58 |
59 | scannerDetectionTest = (s) => {
60 | const {
61 | minLength, avgTimeByChar, onScanButtonLongPressed, scanButtonLongPressThreshold, onScan, onError,
62 | } = this.props
63 | // If string is given, test it
64 | if (s) {
65 | this.firstCharTime = 0
66 | this.lastCharTime = 0
67 | this.stringWriting = s
68 | }
69 |
70 | if (!this.scanButtonCounter) {
71 | this.scanButtonCounter = 1
72 | }
73 |
74 | // If all condition are good (length, time...), call the callback and re-initialize the plugin for next scanning
75 | // Else, just re-initialize
76 | if (this.stringWriting.length >= minLength && this.lastCharTime - this.firstCharTime < this.stringWriting.length * avgTimeByChar) {
77 | if (onScanButtonLongPressed && this.scanButtonCounter > scanButtonLongPressThreshold) onScanButtonLongPressed(this.stringWriting, this.scanButtonCounter)
78 | else if (onScan) onScan(this.stringWriting, this.scanButtonCounter)
79 |
80 | this.initScannerDetection()
81 | return true
82 | }
83 |
84 | let errorMsg = ''
85 | if (this.stringWriting.length < minLength) {
86 | errorMsg = `String length should be greater or equal ${minLength}`
87 | } else {
88 | if (this.lastCharTime - this.firstCharTime > this.stringWriting.length * avgTimeByChar) {
89 | errorMsg = `Average key character time should be less or equal ${avgTimeByChar}ms`
90 | }
91 | }
92 |
93 | if (onError) onError(this.stringWriting, errorMsg)
94 | this.initScannerDetection()
95 | return false
96 | }
97 |
98 | handleKeyPress = (e) => {
99 | const {
100 | onKeyDetect, onReceive, scanButtonKeyCode, stopPropagation, preventDefault, endChar, startChar, timeBeforeScanTest,
101 | } = this.props
102 |
103 | const { target } = e
104 |
105 | if (target instanceof window.HTMLElement && isInput(target)) {
106 | return
107 | }
108 |
109 | // If it's just the button of the scanner, ignore it and wait for the real input
110 | if (scanButtonKeyCode && e.which === scanButtonKeyCode) {
111 | this.scanButtonCounter += 1
112 | // Cancel default
113 | e.preventDefault()
114 | e.stopImmediatePropagation()
115 | }
116 | // Fire keyDetect event in any case!
117 | if (onKeyDetect) onKeyDetect(e)
118 |
119 | if (stopPropagation) e.stopImmediatePropagation()
120 | if (preventDefault) e.preventDefault()
121 |
122 | if (this.firstCharTime && endChar.indexOf(e.which) !== -1) {
123 | e.preventDefault()
124 | e.stopImmediatePropagation()
125 | this.callIsScanner = true
126 | } else if (!this.firstCharTime && startChar.indexOf(e.which) !== -1) {
127 | e.preventDefault()
128 | e.stopImmediatePropagation()
129 | this.callIsScanner = false
130 | } else {
131 | if (typeof (e.which) !== 'undefined') {
132 | this.stringWriting += String.fromCharCode(e.which)
133 | }
134 | this.callIsScanner = false
135 | }
136 |
137 | if (!this.firstCharTime) {
138 | this.firstCharTime = Date.now()
139 | }
140 | this.lastCharTime = Date.now()
141 |
142 | if (this.testTimer) clearTimeout(this.testTimer)
143 | if (this.callIsScanner) {
144 | this.scannerDetectionTest()
145 | this.testTimer = false
146 | } else {
147 | this.testTimer = setTimeout(this.scannerDetectionTest, timeBeforeScanTest)
148 | }
149 |
150 | if (onReceive) onReceive(e)
151 | }
152 |
153 | render() {
154 | if (this.props.testCode) this.scannerDetectionTest(this.props.testCode)
155 | return null
156 | }
157 | }
158 |
159 |
160 | BarcodeScanner.propTypes = {
161 | onScan: PropTypes.func, // Callback after detection of a successfull scanning (scanned string in parameter)
162 | onError: PropTypes.func, // Callback after detection of a unsuccessfull scanning (scanned string in parameter)
163 | onReceive: PropTypes.func, // Callback after receiving and processing a char (scanned char in parameter)
164 | onKeyDetect: PropTypes.func, // Callback after detecting a keyDown (key char in parameter) - in contrast to onReceive, this fires for non-character keys like tab, arrows, etc. too!
165 | timeBeforeScanTest: PropTypes.number, // Wait duration (ms) after keypress event to check if scanning is finished
166 | avgTimeByChar: PropTypes.number, // Average time (ms) between 2 chars. Used to do difference between keyboard typing and scanning
167 | minLength: PropTypes.number, // Minimum length for a scanning
168 | endChar: PropTypes.arrayOf(PropTypes.number), // Chars to remove and means end of scanning
169 | startChar: PropTypes.arrayOf(PropTypes.number), // Chars to remove and means start of scanning
170 | scanButtonKeyCode: PropTypes.number, // Key code of the scanner hardware button (if the scanner button a acts as a key itself)
171 | scanButtonLongPressThreshold: PropTypes.number, // How many times the hardware button should issue a pressed event before a barcode is read to detect a longpress
172 | onScanButtonLongPressed: PropTypes.func, // Callback after detection of a successfull scan while the scan button was pressed and held down
173 | stopPropagation: PropTypes.bool, // Stop immediate propagation on keypress event
174 | preventDefault: PropTypes.bool, // Prevent default action on keypress event
175 | testCode: PropTypes.string, // Test string for simulating
176 | }
177 |
178 | BarcodeScanner.defaultProps = {
179 | timeBeforeScanTest: 100,
180 | avgTimeByChar: 30,
181 | minLength: 6,
182 | endChar: [9, 13],
183 | startChar: [],
184 | scanButtonLongPressThreshold: 3,
185 | stopPropagation: false,
186 | preventDefault: false,
187 | }
188 |
189 | export default BarcodeScanner
--------------------------------------------------------------------------------
/story.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react'
2 | import { storiesOf } from '@storybook/react'
3 | import { action } from '@storybook/addon-actions'
4 | import Reader from './lib'
5 |
6 | class Wrapper extends Component {
7 | render() {
8 | return (
9 |
10 |
14 |
15 | )
16 | }
17 | }
18 |
19 | storiesOf('Barcode scanner', module)
20 | .add('Default', () => )
21 |
--------------------------------------------------------------------------------