all the stuff
This commit is contained in:
parent
758bdb5266
commit
25543dfd88
23
package.json
Normal file
23
package.json
Normal 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
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
12
public/index.html
Normal file
12
public/index.html
Normal 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
15
public/manifest.json
Normal 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
30
src/actions/index.js
Normal 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
34
src/components/app.js
Normal 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>
|
||||
);
|
||||
}
|
||||
}
|
11
src/components/item_details.js
Normal file
11
src/components/item_details.js
Normal 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
|
73
src/components/item_grid.js
Normal file
73
src/components/item_grid.js
Normal 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]} />
|
||||
*/
|
68
src/components/item_grid_element.js
Normal file
68
src/components/item_grid_element.js
Normal 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;
|
36
src/components/next_button.js
Normal file
36
src/components/next_button.js
Normal 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);
|
23
src/components/selection_bar_element.js
Normal file
23
src/components/selection_bar_element.js
Normal 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;
|
33
src/components/selections_bar.js
Normal file
33
src/components/selections_bar.js
Normal 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
15
src/components/top_bar.js
Normal 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
119
src/index.css
Normal 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
17
src/index.js
Normal 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'));
|
10
src/reducers/images_reducer.js
Normal file
10
src/reducers/images_reducer.js
Normal 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
8
src/reducers/index.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import ImagesReducer from './images_reducer';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
images: ImagesReducer,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
Loading…
Reference in New Issue
Block a user