Created sample application
This commit is contained in:
parent
e5536fd07a
commit
576b3fcada
|
@ -0,0 +1,96 @@
|
||||||
|
define([
|
||||||
|
'lodash',
|
||||||
|
'jquery',
|
||||||
|
'react',
|
||||||
|
'ImageSearch.rt'
|
||||||
|
], function (_, $, React, template) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var ImageSearch = React.createClass({
|
||||||
|
|
||||||
|
displayName: 'ImageSearch',
|
||||||
|
mixins: [React.addons.LinkedStateMixin],
|
||||||
|
|
||||||
|
seq: 0,
|
||||||
|
total: 0,
|
||||||
|
hasMore: true,
|
||||||
|
heights: [0, 0, 0],
|
||||||
|
realTerm: 'cats',
|
||||||
|
|
||||||
|
getInitialState: function () {
|
||||||
|
setTimeout(this.search, 0);
|
||||||
|
return {
|
||||||
|
searchTerm: this.realTerm,
|
||||||
|
items: [[], [], []]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
search: function() {
|
||||||
|
this.state.items = [[], [], []];
|
||||||
|
this.total = 0;
|
||||||
|
this.heights = [0, 0, 0];
|
||||||
|
this.hasMore = true;
|
||||||
|
this.realTerm = this.state.searchTerm;
|
||||||
|
this.loadMore();
|
||||||
|
},
|
||||||
|
|
||||||
|
indexOfMin: function(array) {
|
||||||
|
var indexAndMin = _.reduce(array, function(accum, height, index) {
|
||||||
|
return (height < accum.min) ? { i: index, min: height } : accum;
|
||||||
|
}, {i: -1, min: Number.MAX_VALUE});
|
||||||
|
return indexAndMin.i;
|
||||||
|
},
|
||||||
|
|
||||||
|
loadMore: function(done) {
|
||||||
|
done = done || function() {};
|
||||||
|
if (!this.hasMore) {
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var url = 'https://ajax.googleapis.com/ajax/services/search/images?v=1.0&rsz=8&start=' + this.total + '&q=' + this.realTerm + "&callback=?";
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
$.ajax({url: url, dataType: 'jsonp'})
|
||||||
|
.done(function(data){
|
||||||
|
if (!data.responseData) {
|
||||||
|
self.hasMore = false;
|
||||||
|
done();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var results = data.responseData.results;
|
||||||
|
|
||||||
|
var items = _.cloneDeep(self.state.items);
|
||||||
|
|
||||||
|
for (var i = 0; i < results.length; i++) {
|
||||||
|
var result = data.responseData.results[i];
|
||||||
|
var minHeightIndex = self.indexOfMin(self.heights);
|
||||||
|
|
||||||
|
items[minHeightIndex].push({
|
||||||
|
id: self.seq + 1,
|
||||||
|
title: result.titleNoFormatting,
|
||||||
|
url: result.url,
|
||||||
|
ratio: result.width / result.height,
|
||||||
|
originalContext: result.originalContextUrl
|
||||||
|
});
|
||||||
|
|
||||||
|
var relativeHeight = result.height / result.width;
|
||||||
|
self.heights[minHeightIndex] = self.heights[minHeightIndex] + relativeHeight;
|
||||||
|
self.total++;
|
||||||
|
self.seq++;
|
||||||
|
}
|
||||||
|
self.setState({items: items});
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldComponentUpdate: function(nextProps, nextState) {
|
||||||
|
return !_.isEqual(this.state, nextState);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function () {
|
||||||
|
return template.apply(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return ImageSearch;
|
||||||
|
});
|
|
@ -0,0 +1,18 @@
|
||||||
|
<!doctype jsx InfiniteScroll="InfiniteScroll">
|
||||||
|
<div className="innerContainer">
|
||||||
|
<div className="searchbox">
|
||||||
|
<input type="text" valueLink="{this.linkState('searchTerm')}" onKeyDown="(e) => if (e.keyCode == 13) { this.search(); return false; }"></input>
|
||||||
|
<button onClick="() => this.search(); return false;">Search</button>
|
||||||
|
</div>
|
||||||
|
<InfiniteScroll className="fixed" onLoadMore="{this.loadMore}" threshold="{150}">
|
||||||
|
<div rt-repeat="row in [0, 1, 2]" key="{row}">
|
||||||
|
<a rt-repeat="i in this.state.items[row]" href="{i.originalContext}" target="blank" className="container fadeInDown" key="{i.id}">
|
||||||
|
<div style="padding-top: {Math.floor(100/i.ratio)}%; background-color: grey"></div>
|
||||||
|
<div className="imgContainer">
|
||||||
|
<img width="100%" src="{i.url}"/>
|
||||||
|
<div className="title">{i.title}</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</InfiniteScroll>
|
||||||
|
</div>
|
|
@ -0,0 +1,57 @@
|
||||||
|
define([
|
||||||
|
'react',
|
||||||
|
'lodash',
|
||||||
|
'InfiniteScroll'
|
||||||
|
], function (React, _, InfiniteScroll) {
|
||||||
|
'use strict';
|
||||||
|
function onKeyDown1(e) {
|
||||||
|
if (e.keyCode == 13) {
|
||||||
|
this.search();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onClick2() {
|
||||||
|
this.search();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function repeatI3(row, rowIndex, i, iIndex) {
|
||||||
|
return React.DOM.a({
|
||||||
|
'href': i.originalContext,
|
||||||
|
'target': 'blank',
|
||||||
|
'className': 'container fadeInDown',
|
||||||
|
'key': i.id
|
||||||
|
}, React.DOM.div({
|
||||||
|
'style': {
|
||||||
|
paddingTop: Math.floor(100 / i.ratio) + '%',
|
||||||
|
backgroundColor: 'grey'
|
||||||
|
}
|
||||||
|
}), React.DOM.div({ 'className': 'imgContainer' }, React.DOM.img({
|
||||||
|
'width': '100%',
|
||||||
|
'src': i.url
|
||||||
|
}), React.DOM.div({ 'className': 'title' }, i.title)));
|
||||||
|
}
|
||||||
|
function repeatRow4(row, rowIndex) {
|
||||||
|
return React.DOM.div.apply(this, _.flatten([
|
||||||
|
{ 'key': row },
|
||||||
|
_.map(this.state.items[row], repeatI3.bind(this, row, rowIndex))
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
return function () {
|
||||||
|
return React.DOM.div({ 'className': 'innerContainer' }, React.DOM.div({ 'className': 'searchbox' }, React.DOM.input({
|
||||||
|
'type': 'text',
|
||||||
|
'valueLink': this.linkState('searchTerm'),
|
||||||
|
'onKeyDown': onKeyDown1.bind(this)
|
||||||
|
}), React.DOM.button({ 'onClick': onClick2.bind(this) }, 'Search')), InfiniteScroll.apply(this, _.flatten([
|
||||||
|
{
|
||||||
|
'className': 'fixed',
|
||||||
|
'onLoadMore': this.loadMore,
|
||||||
|
'threshold': 150
|
||||||
|
},
|
||||||
|
_.map([
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
], repeatRow4.bind(this))
|
||||||
|
])));
|
||||||
|
};
|
||||||
|
});
|
|
@ -0,0 +1,37 @@
|
||||||
|
define([
|
||||||
|
'lodash',
|
||||||
|
'jquery',
|
||||||
|
'react'
|
||||||
|
], function (_, $, React) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var InfiniteScroll = React.createClass({
|
||||||
|
|
||||||
|
displayName: 'InfiniteScroll',
|
||||||
|
gettingMore: false,
|
||||||
|
|
||||||
|
onLoadMoreFinished: function() {
|
||||||
|
this.gettingMore = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
onScroll: function(evt) {
|
||||||
|
if (!this.props.onLoadMore || this.gettingMore) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var threshold = this.props.threshold || 0;
|
||||||
|
var raw = evt.target;
|
||||||
|
if (raw.scrollTop + raw.offsetHeight + threshold >= raw.scrollHeight) {
|
||||||
|
this.gettingMore = true;
|
||||||
|
this.props.onLoadMore(this.onLoadMoreFinished);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function () {
|
||||||
|
var passedProps = _.omit(this.props, ['onLoadMore', 'threshold', 'children', 'onScroll']);
|
||||||
|
passedProps.onScroll = this.onScroll;
|
||||||
|
return React.DOM.div(passedProps, this.props.children);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return InfiniteScroll;
|
||||||
|
});
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>React templates - Image Search Sample</title>
|
||||||
|
<link rel="stylesheet" href="sample.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form id="main" style="padding:20px 150px;">
|
||||||
|
<!-- the lists panel -->
|
||||||
|
<div id="preloader" style="text-align: center;">Loading...</div>
|
||||||
|
</form>
|
||||||
|
<script data-main="main.js" src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.15/require.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
requirejs.config({
|
||||||
|
// baseUrl: '/',
|
||||||
|
paths: {
|
||||||
|
lodash: 'http://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash',
|
||||||
|
jquery: 'http://code.jquery.com/jquery-1.11.0.min',
|
||||||
|
react: 'http://fb.me/react-with-addons-0.12.0'
|
||||||
|
},
|
||||||
|
shim: {
|
||||||
|
lodash: { exports: '_' },
|
||||||
|
jquery: { exports: '$' },
|
||||||
|
react: { exports: 'React' }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
requirejs(['jquery', 'react', 'ImageSearch'], function ($, React, ImageSearch) {
|
||||||
|
React.renderComponent(ImageSearch(), $('#main').get(0));
|
||||||
|
});
|
||||||
|
|
|
@ -0,0 +1,83 @@
|
||||||
|
.imgContainer {
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
bottom:0;
|
||||||
|
left:0;
|
||||||
|
right:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fixed {
|
||||||
|
height: calc(553px - 53px);
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fixed > div {
|
||||||
|
width: 33%;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.searchbox {
|
||||||
|
text-align:center;
|
||||||
|
height: 53px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: block;
|
||||||
|
background-color: gray;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 10px 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes fadeInDown{
|
||||||
|
0%{
|
||||||
|
opacity:0;
|
||||||
|
-webkit-transform:translate3d(0,-50%,0);
|
||||||
|
transform:translate3d(0,-50%,0)
|
||||||
|
}
|
||||||
|
|
||||||
|
100%{
|
||||||
|
opacity:1;
|
||||||
|
-webkit-transform:none;
|
||||||
|
transform:none
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeInDown{
|
||||||
|
0%{
|
||||||
|
opacity:0;
|
||||||
|
-webkit-transform:translate3d(0,-50%,0);
|
||||||
|
-ms-transform:translate3d(0,-50%,0);
|
||||||
|
transform:translate3d(0,-50%,0)
|
||||||
|
}
|
||||||
|
|
||||||
|
100%{
|
||||||
|
opacity:1;
|
||||||
|
-webkit-transform:none;
|
||||||
|
-ms-transform:none;
|
||||||
|
transform:none
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadeInDown{
|
||||||
|
-webkit-animation:fadeInDown 1s;
|
||||||
|
animation:fadeInDown 1s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: white;
|
||||||
|
background-color: black;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
font-size: 9pt;
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
bottom:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container:hover .title {
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 0.75s;
|
||||||
|
}
|
Loading…
Reference in New Issue