mirror of
https://github.com/serratus/quaggaJS.git
synced 2026-08-13 05:31:37 +08:00
sandbox: finished result view; fixed some styling issues
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
<meta name="author" content="Christoph Oberhofer" />
|
<meta name="author" content="Christoph Oberhofer" />
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="static/style.css" />
|
<link rel="stylesheet" type="text/css" href="static/style.css" />
|
||||||
<meta name="viewport" content="width=device-width; initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
|
||||||
<link href='https://fonts.googleapis.com/css?family=Roboto:400,300,500' rel='stylesheet' type='text/css'>
|
<link href='https://fonts.googleapis.com/css?family=Roboto:400,300,500' rel='stylesheet' type='text/css'>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
@@ -4,13 +4,15 @@ import Drawer from 'material-ui/Drawer';
|
|||||||
import FloatingActionButton from 'material-ui/FloatingActionButton';
|
import FloatingActionButton from 'material-ui/FloatingActionButton';
|
||||||
import Dialog from 'material-ui/Dialog';
|
import Dialog from 'material-ui/Dialog';
|
||||||
import FlatButton from 'material-ui/FlatButton';
|
import FlatButton from 'material-ui/FlatButton';
|
||||||
import {Card, CardTitle, CardText} from 'material-ui/Card';
|
import {Card, CardText} from 'material-ui/Card';
|
||||||
import IconButton from 'material-ui/IconButton';
|
import IconButton from 'material-ui/IconButton';
|
||||||
import TuneIcon from 'material-ui/svg-icons/image/tune';
|
import TuneIcon from 'material-ui/svg-icons/image/tune';
|
||||||
|
|
||||||
import Scanner from './Scanner';
|
import Scanner from './Scanner';
|
||||||
import ScanIcon from './ScanIcon';
|
import ScanIcon from './ScanIcon';
|
||||||
|
import ScannedCode from './ScannedCode';
|
||||||
import ConfigView from './ConfigView';
|
import ConfigView from './ConfigView';
|
||||||
|
import {persist, load} from '../utils/Storage';
|
||||||
|
|
||||||
const cleanConfig = config => {
|
const cleanConfig = config => {
|
||||||
if (typeof config.inputStream.constraints.deviceId === 'number') {
|
if (typeof config.inputStream.constraints.deviceId === 'number') {
|
||||||
@@ -19,50 +21,46 @@ const cleanConfig = config => {
|
|||||||
return config;
|
return config;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const defaultConfig = {
|
||||||
|
frequency: 5,
|
||||||
|
numOfWorkers: 2,
|
||||||
|
locate: true,
|
||||||
|
inputStream: {
|
||||||
|
name: "Live",
|
||||||
|
type: "LiveStream",
|
||||||
|
constraints: {
|
||||||
|
width: 800,
|
||||||
|
height: 600,
|
||||||
|
deviceId: 0,
|
||||||
|
facingMode: "environment",
|
||||||
|
},
|
||||||
|
area: {
|
||||||
|
top: "0%",
|
||||||
|
right: "0%",
|
||||||
|
left: "0%",
|
||||||
|
bottom: "0%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
decoder: {
|
||||||
|
readers: [
|
||||||
|
'ean_reader',
|
||||||
|
'code_39_reader',
|
||||||
|
'code_128_reader',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
locator: {
|
||||||
|
halfSample: true,
|
||||||
|
patchSize: "medium",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export default class App extends React.Component {
|
export default class App extends React.Component {
|
||||||
state = {
|
state = {
|
||||||
drawerOpen: false,
|
drawerOpen: false,
|
||||||
scanning: false,
|
scanning: false,
|
||||||
currentView: 'root',
|
currentView: 'root',
|
||||||
config: {
|
config: load("config") || defaultConfig,
|
||||||
frequency: 5,
|
scannedCodes: load('scannedCodes') || [],
|
||||||
numOfWorkers: 2,
|
|
||||||
locate: true,
|
|
||||||
inputStream: {
|
|
||||||
name: "Live",
|
|
||||||
type: "LiveStream",
|
|
||||||
constraints: {
|
|
||||||
width: 640,
|
|
||||||
height: 480,
|
|
||||||
deviceId: 0,
|
|
||||||
facingMode: "environment",
|
|
||||||
},
|
|
||||||
area: {
|
|
||||||
top: "0%",
|
|
||||||
right: "0%",
|
|
||||||
left: "0%",
|
|
||||||
bottom: "0%",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
decoder: {
|
|
||||||
readers: [
|
|
||||||
'ean_reader',
|
|
||||||
'code_39_reader',
|
|
||||||
'code_128_reader',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
locator: {
|
|
||||||
halfSample: true,
|
|
||||||
patchSize: "medium",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scannedCodes: [{
|
|
||||||
codeResult: {
|
|
||||||
code: "FANAVF1461710",
|
|
||||||
format: "code_128",
|
|
||||||
},
|
|
||||||
timestamp: new Date(),
|
|
||||||
}],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_handleToggle = () => {
|
_handleToggle = () => {
|
||||||
@@ -86,9 +84,20 @@ export default class App extends React.Component {
|
|||||||
|
|
||||||
_handleResult = (result) => {
|
_handleResult = (result) => {
|
||||||
this._stopScanning();
|
this._stopScanning();
|
||||||
this.setState({
|
const scannedCodes =
|
||||||
scannedCodes: [{...result, timestamp: new Date()}]
|
[{
|
||||||
.concat(this.state.scannedCodes)});
|
angle: result.angle,
|
||||||
|
box: result.box,
|
||||||
|
codeResult: {
|
||||||
|
code: result.codeResult.code,
|
||||||
|
direction: result.codeResult.direction,
|
||||||
|
format: result.codeResult.format,
|
||||||
|
},
|
||||||
|
line: result.line,
|
||||||
|
}]
|
||||||
|
.concat(this.state.scannedCodes);
|
||||||
|
this.setState({scannedCodes});
|
||||||
|
persist('scannedCodes', scannedCodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
_navigateTo = (route) => {
|
_navigateTo = (route) => {
|
||||||
@@ -100,7 +109,17 @@ export default class App extends React.Component {
|
|||||||
|
|
||||||
_handleConfigChange = config => {
|
_handleConfigChange = config => {
|
||||||
this.setState({config: cleanConfig(config)});
|
this.setState({config: cleanConfig(config)});
|
||||||
console.log(config);
|
persist("config", config);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleDelete = (scannedCode) => {
|
||||||
|
const index = this.state.scannedCodes.indexOf(scannedCode);
|
||||||
|
if (index !== -1) {
|
||||||
|
const newArray = this.state.scannedCodes.slice();
|
||||||
|
newArray.splice(index, 1);
|
||||||
|
this.setState({scannedCodes: newArray});
|
||||||
|
persist('scannedCodes', newArray);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -119,13 +138,6 @@ export default class App extends React.Component {
|
|||||||
iconElementLeft={<IconButton onTouchTap={this._handleToggle}><TuneIcon /></IconButton>}
|
iconElementLeft={<IconButton onTouchTap={this._handleToggle}><TuneIcon /></IconButton>}
|
||||||
onLeftIconButtonTouchTap={this._handleToggle}
|
onLeftIconButtonTouchTap={this._handleToggle}
|
||||||
/>
|
/>
|
||||||
<FloatingActionButton
|
|
||||||
secondary={true}
|
|
||||||
onMouseDown={this._startScanning}
|
|
||||||
style={{position: 'fixed', right: 0, bottom: 0, margin: '0 1em 1em 0'}}
|
|
||||||
>
|
|
||||||
<ScanIcon />
|
|
||||||
</FloatingActionButton>
|
|
||||||
<Dialog
|
<Dialog
|
||||||
style={{paddingTop: '0px'}}
|
style={{paddingTop: '0px'}}
|
||||||
bodyStyle={{padding: '0.5rem'}}
|
bodyStyle={{padding: '0.5rem'}}
|
||||||
@@ -146,19 +158,27 @@ export default class App extends React.Component {
|
|||||||
onCancel={this._stopScanning} />
|
onCancel={this._stopScanning} />
|
||||||
</Dialog>
|
</Dialog>
|
||||||
<div style={{paddingTop: '64px'}}>
|
<div style={{paddingTop: '64px'}}>
|
||||||
{this.state.currentView === 'root' && this.state.scannedCodes.map((scannedCode, i) => (
|
{this.state.currentView === 'root' && this.state.scannedCodes.length === 0 &&
|
||||||
<Card key={i} style={{margin: '0.5em 0.25em 0em'}}>
|
<Card style={{margin: '0.5em 0.25em 0em'}}>
|
||||||
<CardTitle
|
|
||||||
titleStyle={{textOverflow: 'ellipsis', overflow: 'hidden'}}
|
|
||||||
title={scannedCode.codeResult.code}
|
|
||||||
subtitle={scannedCode.codeResult.format}
|
|
||||||
/>
|
|
||||||
<CardText>
|
<CardText>
|
||||||
Scanned on {scannedCode.timestamp.toLocaleString()}
|
Nothing scanned yet
|
||||||
</CardText>
|
</CardText>
|
||||||
</Card>
|
</Card>
|
||||||
|
}
|
||||||
|
{this.state.currentView === 'root' && this.state.scannedCodes.map((scannedCode, i) => (
|
||||||
|
<ScannedCode
|
||||||
|
key={i}
|
||||||
|
scannedCode={scannedCode}
|
||||||
|
onDelete={this._handleDelete.bind(this, scannedCode)} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<FloatingActionButton
|
||||||
|
secondary={true}
|
||||||
|
onMouseDown={this._startScanning}
|
||||||
|
style={{position: 'fixed', right: 0, bottom: 0, margin: '0 1em 1em 0'}}
|
||||||
|
>
|
||||||
|
<ScanIcon />
|
||||||
|
</FloatingActionButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import SvgIcon from 'material-ui/SvgIcon';
|
|||||||
|
|
||||||
let ActionScanBarcode = (props) => (
|
let ActionScanBarcode = (props) => (
|
||||||
<SvgIcon {...props}>
|
<SvgIcon {...props}>
|
||||||
<path d="M0 4h4v20h-4zM6 4h2v20h-2zM10 4h2v20h-2zM16 4h2v20h-2zM24 4h2v20h-2zM30 4h2v20h-2zM20 4h1v20h-1zM14 4h1v20h-1zM27 4h1v20h-1zM0 26h2v2h-2zM6 26h2v2h-2zM10 26h2v2h-2zM20 26h2v2h-2zM30 26h2v2h-2zM24 26h4v2h-4zM14 26h4v2h-4z"/>
|
<path transform="translate(2, -3)" d="M0 4h4v20h-4zM6 4h2v20h-2zM10 4h2v20h-2zM16 4h2v20h-2zM24 4h2v20h-2zM30 4h2v20h-2zM20 4h1v20h-1zM14 4h1v20h-1zM27 4h1v20h-1zM0 26h2v2h-2zM6 26h2v2h-2zM10 26h2v2h-2zM20 26h2v2h-2zM30 26h2v2h-2zM24 26h4v2h-4zM14 26h4v2h-4z"/>
|
||||||
</SvgIcon>
|
</SvgIcon>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {Card, CardActions, CardText, CardHeader} from 'material-ui/Card';
|
||||||
|
import FlatButton from 'material-ui/FlatButton';
|
||||||
|
import DeleteIcon from 'material-ui/svg-icons/action/delete';
|
||||||
|
import Divider from 'material-ui/Divider';
|
||||||
|
|
||||||
|
const pointGenerator = points => i => `(${points[i][0].toFixed(0)}, ${points[i][1].toFixed(0)})`;
|
||||||
|
const lineGenerator = line => i => `(${line[i].x.toFixed(0)}, ${line[i].y.toFixed(0)})`;
|
||||||
|
|
||||||
|
const renderLineSegments = items => (
|
||||||
|
<div style={{display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', flexBasis: '100%'}}>
|
||||||
|
{items.map((item, i) => (
|
||||||
|
<div key={i} style={{width: `30%`, textAlign: 'center', padding: '0.2rem'}} >{item}</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderBox = box => {
|
||||||
|
const p = pointGenerator(box);
|
||||||
|
const items = [p(1), `→`, p(2), `↑`, ' ', `↓`, p(0), `←`, p(3)];
|
||||||
|
return renderLineSegments(items);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderLine = line => {
|
||||||
|
const l = lineGenerator(line);
|
||||||
|
const items = [l(0), `→`, l(1)];
|
||||||
|
return renderLineSegments(items);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderAngle = angle => (
|
||||||
|
<span>
|
||||||
|
{(angle * 180 / Math.PI).toFixed(2)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderDirection = direction => (
|
||||||
|
<span>
|
||||||
|
{direction === -1 ? 'forward' : `reverse`}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
|
||||||
|
const keyStyle = {
|
||||||
|
fontWeight: 'bold',
|
||||||
|
flex: '0 1 70px',
|
||||||
|
};
|
||||||
|
|
||||||
|
const lineStyle = {
|
||||||
|
marginBottom: '0.5rem',
|
||||||
|
marginTop: '0.5rem',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
};
|
||||||
|
|
||||||
|
const ScannedCode = ({scannedCode, onDelete}) => {
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
style={{margin: '0.5em 0.25em 0em'}}>
|
||||||
|
<CardHeader
|
||||||
|
textStyle={{paddingRight: '20px', maxWidth: '100%', boxSizing: 'border-box'}}
|
||||||
|
titleStyle={{fontSize: '18px', wordWrap: 'break-word'}}
|
||||||
|
title={scannedCode.codeResult.code}
|
||||||
|
subtitle={scannedCode.codeResult.format}
|
||||||
|
actAsExpander={true}
|
||||||
|
showExpandableButton={true}
|
||||||
|
/>
|
||||||
|
<CardText expandable={true}>
|
||||||
|
<div style={lineStyle}>
|
||||||
|
<div style={keyStyle}>Direction: </div><div>{renderDirection(scannedCode.codeResult.direction)}</div>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
<div style={lineStyle}>
|
||||||
|
<div style={keyStyle}>Angle: </div><div>{renderAngle(scannedCode.angle)} deg</div>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
<div style={lineStyle}>
|
||||||
|
<div style={keyStyle}>Line: </div>{renderLine(scannedCode.line)}
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
<div style={lineStyle}>
|
||||||
|
<div style={keyStyle}>Box: </div>
|
||||||
|
{renderBox(scannedCode.box)}
|
||||||
|
</div>
|
||||||
|
</CardText>
|
||||||
|
<CardActions expandable={true}>
|
||||||
|
<FlatButton
|
||||||
|
label=""
|
||||||
|
style={{minWidth: '36px', color: '#aaa'}}
|
||||||
|
onClick={onDelete} icon={<DeleteIcon />} />
|
||||||
|
</CardActions>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
ScannedCode.propTypes = {
|
||||||
|
scannedCode: React.PropTypes.object.isRequired,
|
||||||
|
onDelete: React.PropTypes.func,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ScannedCode;
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
function storageWrapper(cb) {
|
||||||
|
if (typeof window.localStorage !== 'undefined') {
|
||||||
|
return cb(window.localStorage);
|
||||||
|
} else {
|
||||||
|
console.log("localStorage not available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function persist(key, object) {
|
||||||
|
storageWrapper((storage) => {
|
||||||
|
storage.setItem(key, JSON.stringify(object));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export function push(key, object) {
|
||||||
|
storageWrapper((storage) => {
|
||||||
|
const item = storage.getItem() || "[]",
|
||||||
|
parsed = JSON.parse(item);
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
parsed.push(object);
|
||||||
|
storage.setItem(key, JSON.stringify(parsed));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function load(key) {
|
||||||
|
return storageWrapper((storage) => {
|
||||||
|
const item = storage.getItem(key);
|
||||||
|
if (item) {
|
||||||
|
return JSON.parse(item);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user