react-qr-scanner
Advanced tools
Comparing version 0.0.8 to 0.0.9
@@ -75,7 +75,7 @@ { | ||
"react/react-in-jsx-scope": "warn", | ||
"react/require-extension": "warn", | ||
"react/require-extension": "off", | ||
"react/self-closing-comp": "warn", | ||
"react/sort-comp": "warn", | ||
"react/wrap-multilines": "warn" | ||
"react/jsx-wrap-multilines": "warn" | ||
} | ||
} |
@@ -23,3 +23,3 @@ const gulp = require('gulp') | ||
gulp.task('worker', [ 'clean' ], function() { | ||
gulp.task('worker', gulp.series('clean', function() { | ||
return gulp | ||
@@ -30,5 +30,5 @@ .src([ paths.jsQR, paths.worker ]) | ||
.pipe(gulp.dest(paths.destination)) | ||
}) | ||
})) | ||
gulp.task('build', [ 'worker' ], function() { | ||
gulp.task('build', gulp.series('worker', function() { | ||
return gulp | ||
@@ -39,10 +39,10 @@ .src(paths.scripts) | ||
.pipe(gulp.dest(paths.destination)) | ||
}) | ||
})) | ||
// Rerun the task when a file changes | ||
gulp.task('watch', function() { | ||
gulp.watch(paths.scripts, [ 'build' ]) | ||
gulp.watch(paths.scripts, gulp.series('build')) | ||
}) | ||
// The default task (called when you run `gulp` from cli) | ||
gulp.task('default', [ 'build' ]) | ||
gulp.task('default', gulp.series('build')) |
@@ -1,3 +0,1 @@ | ||
"use strict"; | ||
function NoVideoInputDevicesError() { | ||
@@ -9,4 +7,4 @@ this.name = 'NoVideoInputDevicesError'; | ||
NoVideoInputDevicesError.prototype = new Error(); | ||
module.exports = { | ||
export default { | ||
NoVideoInputDevicesError: NoVideoInputDevicesError | ||
}; |
@@ -1,3 +0,1 @@ | ||
"use strict"; | ||
var _require = require('./errors'), | ||
@@ -11,3 +9,3 @@ NoVideoInputDevicesError = _require.NoVideoInputDevicesError; | ||
module.exports = function getDeviceId(facingMode) { | ||
function getDeviceId(facingMode) { | ||
var chooseDeviceId = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultDeviceIdChooser; | ||
@@ -48,2 +46,4 @@ // Get manual deviceId from available devices. | ||
}); | ||
}; | ||
} | ||
export default getDeviceId; |
@@ -1,4 +0,2 @@ | ||
"use strict"; | ||
module.exports = function havePropsChanged(prevProps, nextProps, keys) { | ||
function havePropsChanged(prevProps, nextProps, keys) { | ||
var changedProps = []; | ||
@@ -11,2 +9,4 @@ keys.forEach(function (key) { | ||
return changedProps; | ||
}; | ||
} | ||
export default havePropsChanged; |
{ | ||
"name": "react-qr-scanner", | ||
"version": "0.0.8", | ||
"version": "0.0.9", | ||
"description": "A react component for reading QR codes from the webcam.", | ||
@@ -31,31 +31,33 @@ "main": "./lib/index.js", | ||
"devDependencies": { | ||
"@babel/cli": "^7.0.0", | ||
"@babel/core": "^7.1.2", | ||
"@babel/plugin-proposal-class-properties": "^7.0.0", | ||
"@babel/plugin-proposal-decorators": "^7.0.0", | ||
"@babel/plugin-proposal-do-expressions": "^7.0.0", | ||
"@babel/plugin-proposal-export-default-from": "^7.0.0", | ||
"@babel/plugin-proposal-export-namespace-from": "^7.0.0", | ||
"@babel/plugin-proposal-function-bind": "^7.0.0", | ||
"@babel/plugin-proposal-function-sent": "^7.0.0", | ||
"@babel/plugin-proposal-json-strings": "^7.0.0", | ||
"@babel/plugin-proposal-logical-assignment-operators": "^7.0.0", | ||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0", | ||
"@babel/plugin-proposal-numeric-separator": "^7.0.0", | ||
"@babel/plugin-proposal-optional-chaining": "^7.0.0", | ||
"@babel/plugin-proposal-pipeline-operator": "^7.0.0", | ||
"@babel/plugin-proposal-throw-expressions": "^7.0.0", | ||
"@babel/plugin-syntax-dynamic-import": "^7.0.0", | ||
"@babel/plugin-syntax-import-meta": "^7.0.0", | ||
"@babel/preset-env": "^7.1.0", | ||
"@babel/preset-react": "^7.0.0", | ||
"@storybook/addon-actions": "^3.4.11", | ||
"@storybook/cli": "^3.4.11", | ||
"@storybook/react": "^3.4.11", | ||
"@babel/cli": "^7.8.4", | ||
"@babel/core": "^7.9.6", | ||
"@babel/plugin-proposal-class-properties": "^7.8.3", | ||
"@babel/plugin-proposal-decorators": "^7.8.3", | ||
"@babel/plugin-proposal-do-expressions": "^7.8.3", | ||
"@babel/plugin-proposal-export-default-from": "^7.8.3", | ||
"@babel/plugin-proposal-export-namespace-from": "^7.8.3", | ||
"@babel/plugin-proposal-function-bind": "^7.8.3", | ||
"@babel/plugin-proposal-function-sent": "^7.8.3", | ||
"@babel/plugin-proposal-json-strings": "^7.8.3", | ||
"@babel/plugin-proposal-logical-assignment-operators": "^7.8.3", | ||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", | ||
"@babel/plugin-proposal-numeric-separator": "^7.8.3", | ||
"@babel/plugin-proposal-optional-chaining": "^7.9.0", | ||
"@babel/plugin-proposal-pipeline-operator": "^7.8.3", | ||
"@babel/plugin-proposal-throw-expressions": "^7.8.3", | ||
"@babel/plugin-syntax-dynamic-import": "^7.8.3", | ||
"@babel/plugin-syntax-import-meta": "^7.8.3", | ||
"@babel/preset-env": "^7.9.6", | ||
"@babel/preset-react": "^7.9.4", | ||
"@storybook/addon-actions": "^5.3.18", | ||
"@storybook/cli": "^5.3.18", | ||
"@storybook/react": "^5.3.18", | ||
"babel-core": "^7.0.0-bridge.0", | ||
"babel-eslint": "^10.0.1", | ||
"del": "^3.0.0", | ||
"eslint": "^5.6.1", | ||
"eslint-plugin-react": "^7.11.1", | ||
"gulp": "^3.9.1", | ||
"babel-eslint": "^10.1.0", | ||
"babel-loader": "^8.1.0", | ||
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", | ||
"del": "^5.1.0", | ||
"eslint": "^6.8.0", | ||
"eslint-plugin-react": "^7.19.0", | ||
"gulp": "^4.0.2", | ||
"gulp-babel": "^8.0.0", | ||
@@ -67,8 +69,8 @@ "gulp-butternut": "^1.0.0", | ||
"dependencies": { | ||
"jsqr": "^1.1.1", | ||
"prop-types": "^15.6.2", | ||
"react": "^16.5.2", | ||
"react-dom": "^16.5.2", | ||
"webrtc-adapter": "^6.4.0" | ||
"jsqr": "^1.3.1", | ||
"prop-types": "^15.7.2", | ||
"react": "^16.13.1", | ||
"react-dom": "^16.13.1", | ||
"webrtc-adapter": "^7.5.1" | ||
} | ||
} |
@@ -7,4 +7,4 @@ function NoVideoInputDevicesError() { | ||
module.exports = { | ||
export default { | ||
NoVideoInputDevicesError, | ||
} |
@@ -10,3 +10,3 @@ const { NoVideoInputDevicesError } = require('./errors') | ||
module.exports = function getDeviceId(facingMode, chooseDeviceId = defaultDeviceIdChooser) { | ||
function getDeviceId(facingMode, chooseDeviceId = defaultDeviceIdChooser) { | ||
// Get manual deviceId from available devices. | ||
@@ -47,1 +47,3 @@ return new Promise((resolve, reject) => { | ||
} | ||
export default getDeviceId |
@@ -1,2 +0,2 @@ | ||
module.exports = function havePropsChanged(prevProps, nextProps, keys) { | ||
function havePropsChanged(prevProps, nextProps, keys) { | ||
const changedProps = [] | ||
@@ -10,1 +10,3 @@ keys.forEach(key => { | ||
} | ||
export default havePropsChanged |
109
src/index.js
@@ -1,6 +0,5 @@ | ||
const React = require('react') | ||
const { Component } = React | ||
const PropTypes = require('prop-types') | ||
const getDeviceId = require('./getDeviceId') | ||
const havePropsChanged = require('./havePropsChanged') | ||
import React, { Component } from 'react' | ||
import PropTypes from 'prop-types' | ||
import getDeviceId from './getDeviceId' | ||
import havePropsChanged from './havePropsChanged' | ||
@@ -11,2 +10,3 @@ // Require adapter to support older browser implementations | ||
// Inline worker.js as a string value of workerBlob. | ||
// eslint-disable-next-line no-undef | ||
const workerBlob = new Blob([__inline('../lib/worker.js')], { | ||
@@ -19,27 +19,8 @@ type: 'application/javascript', | ||
module.exports = class Reader extends Component { | ||
static propTypes = { | ||
onScan: PropTypes.func.isRequired, | ||
onError: PropTypes.func.isRequired, | ||
onLoad: PropTypes.func, | ||
onImageLoad: PropTypes.func, | ||
delay: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]), | ||
facingMode: PropTypes.oneOf(['rear', 'front']), | ||
legacyMode: PropTypes.bool, | ||
maxImageSize: PropTypes.number, | ||
style: PropTypes.any, | ||
className: PropTypes.string, | ||
chooseDeviceId: PropTypes.func, | ||
}; | ||
static defaultProps = { | ||
delay: 500, | ||
maxImageSize: 1000, | ||
facingMode: 'rear', | ||
}; | ||
class Reader extends Component { | ||
els = {}; | ||
constructor(props) { | ||
super(props) | ||
this.els = {} | ||
// Bind function to the class | ||
@@ -58,2 +39,3 @@ this.initiate = this.initiate.bind(this) | ||
} | ||
componentDidMount() { | ||
@@ -70,3 +52,4 @@ // Initiate web worker execute handler according to mode. | ||
} | ||
componentWillReceiveProps(nextProps) { | ||
shouldComponentUpdate(nextProps) { | ||
// React according to change in props | ||
@@ -76,14 +59,14 @@ const changedProps = havePropsChanged(this.props, nextProps, propsKeys) | ||
for (const prop of changedProps) { | ||
if (prop == 'facingMode') { | ||
if (prop === 'facingMode') { | ||
this.clearComponent() | ||
this.initiate(nextProps) | ||
break | ||
} else if (prop == 'delay') { | ||
if (this.props.delay == false && !nextProps.legacyMode) { | ||
} else if (prop === 'delay') { | ||
if (this.props.delay === false && !nextProps.legacyMode) { | ||
this.timeout = setTimeout(this.check, nextProps.delay) | ||
} | ||
if (nextProps.delay == false) { | ||
if (nextProps.delay === false) { | ||
clearTimeout(this.timeout) | ||
} | ||
} else if (prop == 'legacyMode') { | ||
} else if (prop === 'legacyMode') { | ||
if (this.props.legacyMode && !nextProps.legacyMode) { | ||
@@ -99,8 +82,7 @@ this.clearComponent() | ||
} | ||
} | ||
shouldComponentUpdate(nextProps) { | ||
// Only render when the `propsKeys` have changed. | ||
const changedProps = havePropsChanged(this.props, nextProps, propsKeys) | ||
return changedProps.length > 0 | ||
} | ||
componentWillUnmount() { | ||
@@ -114,2 +96,3 @@ // Stop web-worker and clear the component | ||
} | ||
clearComponent() { | ||
@@ -131,2 +114,3 @@ // Remove all event listeners and variables | ||
} | ||
initiate(props = this.props) { | ||
@@ -148,2 +132,3 @@ const { onError, facingMode, chooseDeviceId } = props | ||
} | ||
handleVideo(stream) { | ||
@@ -153,3 +138,3 @@ const { preview } = this.els | ||
// Handle different browser implementations of MediaStreams as src | ||
if(preview.srcObject !== undefined){ | ||
if (preview.srcObject !== undefined) { | ||
preview.srcObject = stream | ||
@@ -175,2 +160,3 @@ } else if (preview.mozSrcObject !== undefined) { | ||
} | ||
handleLoadStart() { | ||
@@ -181,7 +167,7 @@ const { delay, onLoad } = this.props | ||
if(typeof onLoad == 'function') { | ||
if (typeof onLoad === 'function') { | ||
onLoad() | ||
} | ||
if (typeof delay == 'number') { | ||
if (typeof delay === 'number') { | ||
this.timeout = setTimeout(this.check, delay) | ||
@@ -193,2 +179,3 @@ } | ||
} | ||
check() { | ||
@@ -207,3 +194,3 @@ const { legacyMode, maxImageSize, delay } = this.props | ||
const greatestSize = width > height ? width : height | ||
if(greatestSize > maxImageSize){ | ||
if (greatestSize > maxImageSize) { | ||
const ratio = maxImageSize / greatestSize | ||
@@ -233,12 +220,21 @@ height = ratio * height | ||
} | ||
handleWorkerMessage(e) { | ||
const { onScan, legacyMode, delay } = this.props | ||
const { preview, canvas, img } = this.els | ||
const decoded = e.data | ||
if (decoded && decoded.data) { | ||
const ctx = canvas.getContext('2d') | ||
ctx.drawImage(legacyMode ? img : preview, 0, 0, canvas.width, canvas.height) | ||
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height) | ||
onScan(decoded.data, imageData) | ||
} else { | ||
onScan(null) | ||
} | ||
onScan((decoded && decoded.data) || null) | ||
if (!legacyMode && typeof delay == 'number' && this.worker) { | ||
if (!legacyMode && typeof delay === 'number' && this.worker) { | ||
this.timeout = setTimeout(this.check, delay) | ||
} | ||
} | ||
initiateLegacyMode() { | ||
@@ -252,6 +248,7 @@ this.reader = new FileReader() | ||
if(typeof this.props.onLoad == 'function') { | ||
if (typeof this.props.onLoad === 'function') { | ||
this.props.onLoad() | ||
} | ||
} | ||
handleInputChange(e) { | ||
@@ -261,2 +258,3 @@ const selectedImg = e.target.files[0] | ||
} | ||
handleReaderLoad(e) { | ||
@@ -266,2 +264,3 @@ // Set selected image blob as img source | ||
} | ||
openImageDialog() { | ||
@@ -271,2 +270,3 @@ // Function to be executed by parent in user action context to trigger img file uploader | ||
} | ||
setRefFactory(key) { | ||
@@ -277,2 +277,3 @@ return element => { | ||
} | ||
render() { | ||
@@ -307,1 +308,23 @@ const { style, className, onImageLoad, legacyMode } = this.props | ||
} | ||
Reader.propTypes = { | ||
onScan: PropTypes.func.isRequired, | ||
onError: PropTypes.func.isRequired, | ||
onLoad: PropTypes.func, | ||
onImageLoad: PropTypes.func, | ||
delay: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]), | ||
facingMode: PropTypes.oneOf(['rear', 'front']), | ||
legacyMode: PropTypes.bool, | ||
maxImageSize: PropTypes.number, | ||
style: PropTypes.any, | ||
className: PropTypes.string, | ||
chooseDeviceId: PropTypes.func, | ||
} | ||
Reader.defaultProps = { | ||
delay: 500, | ||
maxImageSize: 1000, | ||
facingMode: 'rear', | ||
} | ||
export default Reader |
// jsQR is concatenated by gulp | ||
self.addEventListener('message', function(e) { | ||
self.addEventListener('message', function (e) { | ||
// eslint-disable-next-line no-undef | ||
const decoded = jsQR( | ||
@@ -5,0 +6,0 @@ e.data.data, |
111
story.js
@@ -1,5 +0,5 @@ | ||
import React, { Component } from 'react' | ||
import { storiesOf } from '@storybook/react' | ||
import { action } from '@storybook/addon-actions' | ||
import Reader from './lib' | ||
import React, { Component } from "react"; | ||
import { storiesOf } from "@storybook/react"; | ||
import { action } from '@storybook/addon-actions'; | ||
import Reader from "./lib"; | ||
@@ -9,6 +9,43 @@ class Wrapper extends Component { | ||
super(props) | ||
this.state = { facingMode: 'front', delay: 500 } | ||
this.state = { cameraId: undefined, delay: 500, devices: [], loading: false } | ||
} | ||
componentWillMount() { | ||
const { selectFacingMode } = this.props | ||
if (navigator && selectFacingMode) { | ||
this.setState({ | ||
loading: true, | ||
}) | ||
navigator.mediaDevices.enumerateDevices() | ||
.then((devices) => { | ||
const videoSelect = [] | ||
devices.forEach((device) => { | ||
if (device.kind === 'videoinput') { | ||
videoSelect.push(device) | ||
} | ||
}) | ||
return videoSelect | ||
}) | ||
.then((devices) => { | ||
this.setState({ | ||
cameraId: devices[0].deviceId, | ||
devices, | ||
loading: false, | ||
}) | ||
}) | ||
.catch((error) => { | ||
console.log(error) | ||
}) | ||
} | ||
} | ||
selectCamera = () => { | ||
return this.state.cameraId | ||
} | ||
render() { | ||
const { selectFacingMode, selectDelay, legacyMode } = this.props | ||
const { cameraId, devices } = this.state | ||
@@ -19,28 +56,35 @@ const previewStyle = { width: 320 } | ||
{ | ||
selectFacingMode && ( | ||
<select | ||
onChange={e => this.setState({ facingMode: e.target.value })} | ||
> | ||
<option value="front">Front</option> | ||
<option value="rear">Rear</option> | ||
</select> | ||
) | ||
selectFacingMode && devices.length && ( | ||
<select | ||
onChange={e => { | ||
const value = e.target.value | ||
this.setState({ cameraId: undefined }, () => { | ||
this.setState({ cameraId: value }) | ||
}) | ||
}} | ||
> | ||
{devices.map((deviceInfo, index) => ( | ||
<React.Fragment key={deviceInfo.deviceId}><option value={deviceInfo.deviceId}>{deviceInfo.label || `camera ${index}`}</option></React.Fragment> | ||
))} | ||
</select> | ||
) | ||
} | ||
{ | ||
selectDelay && ( | ||
<div> | ||
<button onClick={() => this.setState({ delay: false })}> | ||
Disable Delay | ||
<div> | ||
<button onClick={() => this.setState({ delay: false })}> | ||
Disable Delay | ||
</button> | ||
<input | ||
placeholder="Delay in ms" | ||
type="number" | ||
value={this.state.delay} | ||
onChange={e => | ||
this.setState({ delay: parseInt(e.target.value) })} | ||
/> | ||
</div> | ||
) | ||
<input | ||
placeholder="Delay in ms" | ||
type="number" | ||
value={this.state.delay} | ||
onChange={e => | ||
this.setState({ delay: parseInt(e.target.value) })} | ||
/> | ||
</div> | ||
) | ||
} | ||
<Reader | ||
{(cameraId || !selectFacingMode) && (<Reader | ||
chooseDeviceId={this.selectCamera} | ||
style={previewStyle} | ||
@@ -52,3 +96,2 @@ onError={action('Error')} | ||
ref="reader" | ||
facingMode={this.state.facingMode} | ||
legacyMode={legacyMode} | ||
@@ -58,9 +101,9 @@ maxImageSize={1000} | ||
className="reader-container" | ||
/> | ||
/>)} | ||
{ | ||
legacyMode && ( | ||
<button onClick={() => this.refs.reader.openImageDialog()}> | ||
Open Image Dialog | ||
<button onClick={() => this.refs.reader.openImageDialog()}> | ||
Open Image Dialog | ||
</button> | ||
) | ||
) | ||
} | ||
@@ -72,6 +115,6 @@ </div> | ||
storiesOf('QR Reader', module) | ||
.add('FacingMode not specified', () => <Wrapper />) | ||
.add('Choose facingMode', () => <Wrapper selectFacingMode />) | ||
storiesOf("QR Reader", module) | ||
.add('Camera not specified', () => <Wrapper />) | ||
.add('Choose camera', () => <Wrapper selectFacingMode />) | ||
.add('Legacy mode', () => <Wrapper legacyMode />) | ||
.add('Choose delay', () => <Wrapper selectDelay />) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
6397780
35
15321
1
2
2
35
2
80
+ Addedwebrtc-adapter@7.7.1(transitive)
- Removedwebrtc-adapter@6.4.8(transitive)
Updatedjsqr@^1.3.1
Updatedprop-types@^15.7.2
Updatedreact@^16.13.1
Updatedreact-dom@^16.13.1
Updatedwebrtc-adapter@^7.5.1