all the stuff

This commit is contained in:
Jacob 2017-11-06 13:23:20 +01:00
parent 758bdb5266
commit 25543dfd88
19 changed files with 9546 additions and 0 deletions

2164
README.md

File diff suppressed because it is too large Load Diff

23
package.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "image-tagger",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.17.0",
"google-images": "^2.1.0",
"google-search": "^0.0.5",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-redux": "^5.0.6",
"react-scripts": "1.0.14",
"react-transition-group": "1.x",
"redux": "^3.7.2",
"redux-promise": "^0.5.3"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

12
public/index.html Normal file
View File

@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>React App</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous">
</head>
<body>
<div class="container"></div>
</body>
</html>

15
public/manifest.json Normal file
View File

@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "192x192",
"type": "image/png"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

30
src/actions/index.js Normal file
View File

@ -0,0 +1,30 @@
const API_KEY = "AIzaSyBDy61ywpgUiR9yhNdmrUJIiZ6igyzmgKg";
const engineId = "001027689024880574849:8zadv8ivrek";
const GoogleImages = require('google-images');
const client = new GoogleImages( engineId, API_KEY );
export const FETCH_IMAGES = 'FETCH_IMAGES';
export function fetchImages (page) {
const request = client.search("shoes", {page}).then(images =>
images.map(image => image.url)
);
/*const request = ['https://www.pediwear.co.uk/image/glossary/7.jpg',
'https://www.pediwear.co.uk/image/glossary/7.jpg',
'https://www.pediwear.co.uk/image/glossary/7.jpg',
'https://www.pediwear.co.uk/image/glossary/7.jpg',
'https://www.pediwear.co.uk/image/glossary/7.jpg',
'https://www.pediwear.co.uk/image/glossary/7.jpg',
'https://www.pediwear.co.uk/image/glossary/7.jpg',
'https://www.pediwear.co.uk/image/glossary/7.jpg',
'https://www.pediwear.co.uk/image/glossary/7.jpg',
'https://www.pediwear.co.uk/image/glossary/7.jpg',];*/
return {
type: FETCH_IMAGES,
payload: request
};
}

34
src/components/app.js Normal file
View File

@ -0,0 +1,34 @@
import React, { Component } from 'react';
import TopBar from './top_bar';
import ItemDetails from './item_details';
import ItemGrid from './item_grid';
import NextButton from './next_button';
import SelectionsBar from './selections_bar'
export default class App extends Component {
constructor (props) {
super(props);
this.state = { selectedImages: [] }
}
addImage (image) {
const newSelection = this.state.selectedImages;
newSelection.push(image)
this.setState({ selectedImages: newSelection })
}
render() {
return (
<div className="row outer no-gutters">
<div className="col no-gutters">
<TopBar />
<ItemDetails />
<ItemGrid addImage={ this.addImage.bind(this) }/>
<NextButton />
</div>
<SelectionsBar selectedImages={ this.state.selectedImages }/>
</div>
);
}
}

View File

@ -0,0 +1,11 @@
import React from 'react';
const ItemDetails = () => {
return (
<div className="itemdetails row">
<div className="col">Some interesting details.</div>
</div>
)
}
export default ItemDetails

View File

@ -0,0 +1,73 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import ItemGridElement from './item_grid_element';
import { fetchImages } from '../actions/index';
class ItemGrid extends Component {
constructor(props) {
super(props);
this.props.fetchImages(1)
}
renderImage(i) {
return <ItemGridElement addImage={this.props.addImage} clickable={true} url={i} />
}
render () {
return (
<div className='itemgrid'>
<div className="row">
{this.props.images.map((o, i) => {
if (i < 5 ) {
return this.renderImage(o)
}
return ""
})}
</div>
<div className="row">
{this.props.images.map((o, i) => {
if (i > 4 ) {
return this.renderImage(o)
}
return ""
} )}
</div>
</div>
)
}
}
function mapStateToProps( state ) {
return {
images: state.images
}
}
function mapDispatchToProps (dispatch) {
return bindActionCreators ({ fetchImages: fetchImages }, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(ItemGrid);
/*
<div className="row">
<ItemGridElement addImage={this.props.addImage} clickable={true} url={this.props.images[0]} />
<ItemGridElement addImage={this.props.addImage} clickable={true} url={this.props.images[1]} />
<ItemGridElement addImage={this.props.addImage} clickable={true} url={this.props.images[2]} />
<ItemGridElement addImage={this.props.addImage} clickable={true} url={this.props.images[3]} />
<ItemGridElement addImage={this.props.addImage} clickable={true} url={this.props.images[4]} />
</div>
<div className="row">
<ItemGridElement addImage={this.props.addImage} clickable={true} url={this.props.images[5]} />
<ItemGridElement addImage={this.props.addImage} clickable={true} url={this.props.images[6]} />
<ItemGridElement addImage={this.props.addImage} clickable={true} url={this.props.images[7]} />
<ItemGridElement addImage={this.props.addImage} clickable={true} url={this.props.images[8]} />
<ItemGridElement addImage={this.props.addImage} clickable={true} url={this.props.images[9]} />
*/

View File

@ -0,0 +1,68 @@
import React, { Component } from 'react';
class ItemGridElement extends Component {
constructor ( props ) {
super(props)
this.state = {
clickable: true
}
this.onSelectImage = this.onSelectImage.bind(this);
if (!this.props.clickable) {
this.setState({clickable: false})
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.url !== this.props.url) {
this.setState({clickable: true});
}
}
onSelectImage () {
if (this.state.clickable) {
if (this.props.addImage !== null){
this.props.addImage(this.props.url);
}
this.setState({clickable: !this.state.clickable});
}
}
render () {
const notClickableOptions = {
borderWidth: '2px',
borderColor: '#3333cc',
boxShadow: "0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19)"
}
const clickableOptions = {
borderWidth: '1px',
borderColor: 'LightGrey'
}
if (!this.props.url) {
return (
<div className="col itemElementLoading">
Loading...
</div>
)
}
return (
<div className='col gridElement'>
<img
src={ this.props.url }
onClick={ this.onSelectImage }
alt=""
width="125"
height="125"
className="itemElementImage rounded"
style={ this.state.clickable ? clickableOptions : notClickableOptions }
/>
</div>
);
}
}
export default ItemGridElement;

View File

@ -0,0 +1,36 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchImages } from '../actions/index';
class NextButton extends Component {
constructor (props) {
super(props);
this.state = { currentPage: 1}
this.getNextImages = this.getNextImages.bind(this);
}
getNextImages () {
console.log(this.state.currentPage);
this.setState({currentPage: this.state.currentPage + 20});
this.props.fetchImages(this.state.currentPage);
}
render () {
return (
<div className="row">
<div onClick={this.getNextImages}className="nextbutton text-center fixed-bottom col">
Next
</div>
</div>
)
}
}
function mapDispatchToProps (dispatch) {
return bindActionCreators ({ fetchImages: fetchImages }, dispatch);
}
export default connect(null, mapDispatchToProps)(NextButton);

View File

@ -0,0 +1,23 @@
import React, { Component } from 'react';
class SelectionBarElement extends Component {
render () {
if (!this.props.url) {
return (
<div className="col itemElementLoading">
Loading...
</div>
)
}
return (
<div>
<img src={ this.props.url } alt="" width="125" height="125" className="selectionbarimage rounded" />
</div>
);
}
}
export default SelectionBarElement;

View File

@ -0,0 +1,33 @@
import React, { Component } from 'react';
import SelectionBarElement from './selection_bar_element';
class SelectionsBar extends Component {
renderSelectedImages (image) {
return (
<SelectionBarElement
key={image}
addImage={()=>{}}
url={image} />
)
}
render (){
if ( this.props.selectedImages.length === 0 ) {
return (
<div className='selectionsbarempty col-sm-3 col-md-auto no-gutter'></div>
)
}
return (
<div className='selectionsbar col-sm-3 no-gutter'>
<ul className='row-sm-11 selectionsbarinner list-group pre-scrollable'>
{this.props.selectedImages.slice().reverse().map(this.renderSelectedImages)}
</ul>
<div className='row-sm-1 selectionsbarempty' />
</div>
)
}
}
export default SelectionsBar;

15
src/components/top_bar.js Normal file
View File

@ -0,0 +1,15 @@
import React from 'react';
const TopBar = () => {
return (
<div className="topbar fixed-top">
<div className="row">
<div className="col text-right">Left</div>
<div className="col text-center">Center</div>
<div className="col text-left">Right</div>
</div>
</div>
)
}
export default TopBar

119
src/index.css Normal file
View File

@ -0,0 +1,119 @@
html, body {
height: 100%;
background-color: WhiteSmoke;
text-align: center;
}
.container {
height: 100%;
/*background-color: yellow;*/
max-width: 100%;
max-height: 100%;
padding: 0;
}
.topbar {
padding-top: 20px;
padding-bottom: 20px;
width: 100%;
overflow-x: hidden;
background-color: BlueViolet;
color: white;
}
.col-sm {
text-align: center;
}
.itemdetails{
width:100%;
margin: 10px;
padding: 10px;
padding-top:90px;
text-align:center;
}
.gridElement {
width: 125px;
height: 125px;
margin: 10px;
}
.nextbutton{
padding-top: 20px;
padding-bottom: 20px;
background-color: BlueViolet;
color: white;
}
.nextbutton:hover{
background-color: #9999ff;
}
.nextbutton:active{
background-color: #666699
}
.itemElementImage {
width: 125px;
max-width: 125px;
height: 125px;
text-align: center;
border-style: solid;
border-width: 1px;
border-color: LightGrey;
}
.itemElementLoading {
padding-top: 40px;
padding-bottom: 40px;
text-align: center;
border-style: solid;
border-width: 1px;
border-color: LightGrey;
}
.itemElementImage:hover {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.itemElementLoading:hover {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.selectionsbar {
text-align: center;
height: 100vh;
padding-top: 15vh;
padding-bottom: 30vh;
margin-left: 50px;
background-color: #ccccff;
}
.selectionsbarinner{
min-height: 80vh;
}
.selectionsbarempty{
}
.selectionbarimage{
width: 125px;
height: 125px;
padding: 5px;
margin-top: 20px;
margin-bottom: 20px;
min-height: 125px;
}
.no-gutters {
margin-right: 0;
margin-left: 0;
}
.outer {
max-width:100%;
width:100%;
height: 100vh;
}

17
src/index.js Normal file
View File

@ -0,0 +1,17 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import './index.css';
import { createStore, applyMiddleware } from 'redux';
import ReduxPromise from 'redux-promise';
import App from './components/app';
import reducers from './reducers';
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<App className="no-gutter" />
</Provider>
, document.querySelector('.container'));

View File

@ -0,0 +1,10 @@
import { FETCH_IMAGES } from '../actions/index';
export default function (state = [], action) {
switch (action.type) {
case FETCH_IMAGES:
return action.payload;
default:
return state;
}
}

8
src/reducers/index.js Normal file
View File

@ -0,0 +1,8 @@
import { combineReducers } from 'redux';
import ImagesReducer from './images_reducer';
const rootReducer = combineReducers({
images: ImagesReducer,
});
export default rootReducer;

6855
yarn.lock Normal file

File diff suppressed because it is too large Load Diff