mirror of
https://github.com/serratus/quaggaJS.git
synced 2026-08-12 21:21:42 +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" />
|
||||
|
||||
<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'>
|
||||
|
||||
</head>
|
||||
|
||||
@@ -4,13 +4,15 @@ import Drawer from 'material-ui/Drawer';
|
||||
import FloatingActionButton from 'material-ui/FloatingActionButton';
|
||||
import Dialog from 'material-ui/Dialog';
|
||||
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 TuneIcon from 'material-ui/svg-icons/image/tune';
|
||||
|
||||
import Scanner from './Scanner';
|
||||
import ScanIcon from './ScanIcon';
|
||||
import ScannedCode from './ScannedCode';
|
||||
import ConfigView from './ConfigView';
|
||||
import {persist, load} from '../utils/Storage';
|
||||
|
||||
const cleanConfig = config => {
|
||||
if (typeof config.inputStream.constraints.deviceId === 'number') {
|
||||
@@ -19,50 +21,46 @@ const cleanConfig = 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 {
|
||||
state = {
|
||||
drawerOpen: false,
|
||||
scanning: false,
|
||||
currentView: 'root',
|
||||
config: {
|
||||
frequency: 5,
|
||||
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(),
|
||||
}],
|
||||
config: load("config") || defaultConfig,
|
||||
scannedCodes: load('scannedCodes') || [],
|
||||
};
|
||||
|
||||
_handleToggle = () => {
|
||||
@@ -86,9 +84,20 @@ export default class App extends React.Component {
|
||||
|
||||
_handleResult = (result) => {
|
||||
this._stopScanning();
|
||||
this.setState({
|
||||
scannedCodes: [{...result, timestamp: new Date()}]
|
||||
.concat(this.state.scannedCodes)});
|
||||
const 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) => {
|
||||
@@ -100,7 +109,17 @@ export default class App extends React.Component {
|
||||
|
||||
_handleConfigChange = 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() {
|
||||
@@ -119,13 +138,6 @@ export default class App extends React.Component {
|
||||
iconElementLeft={<IconButton onTouchTap={this._handleToggle}><TuneIcon /></IconButton>}
|
||||
onLeftIconButtonTouchTap={this._handleToggle}
|
||||
/>
|
||||
<FloatingActionButton
|
||||
secondary={true}
|
||||
onMouseDown={this._startScanning}
|
||||
style={{position: 'fixed', right: 0, bottom: 0, margin: '0 1em 1em 0'}}
|
||||
>
|
||||
<ScanIcon />
|
||||
</FloatingActionButton>
|
||||
<Dialog
|
||||
style={{paddingTop: '0px'}}
|
||||
bodyStyle={{padding: '0.5rem'}}
|
||||
@@ -146,19 +158,27 @@ export default class App extends React.Component {
|
||||
onCancel={this._stopScanning} />
|
||||
</Dialog>
|
||||
<div style={{paddingTop: '64px'}}>
|
||||
{this.state.currentView === 'root' && this.state.scannedCodes.map((scannedCode, i) => (
|
||||
<Card key={i} style={{margin: '0.5em 0.25em 0em'}}>
|
||||
<CardTitle
|
||||
titleStyle={{textOverflow: 'ellipsis', overflow: 'hidden'}}
|
||||
title={scannedCode.codeResult.code}
|
||||
subtitle={scannedCode.codeResult.format}
|
||||
/>
|
||||
{this.state.currentView === 'root' && this.state.scannedCodes.length === 0 &&
|
||||
<Card style={{margin: '0.5em 0.25em 0em'}}>
|
||||
<CardText>
|
||||
Scanned on {scannedCode.timestamp.toLocaleString()}
|
||||
Nothing scanned yet
|
||||
</CardText>
|
||||
</Card>
|
||||
}
|
||||
{this.state.currentView === 'root' && this.state.scannedCodes.map((scannedCode, i) => (
|
||||
<ScannedCode
|
||||
key={i}
|
||||
scannedCode={scannedCode}
|
||||
onDelete={this._handleDelete.bind(this, scannedCode)} />
|
||||
))}
|
||||
</div>
|
||||
<FloatingActionButton
|
||||
secondary={true}
|
||||
onMouseDown={this._startScanning}
|
||||
style={{position: 'fixed', right: 0, bottom: 0, margin: '0 1em 1em 0'}}
|
||||
>
|
||||
<ScanIcon />
|
||||
</FloatingActionButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import SvgIcon from 'material-ui/SvgIcon';
|
||||
|
||||
let ActionScanBarcode = (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>
|
||||
);
|
||||
|
||||
|
||||
@@ -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