diff --git a/client/package.json b/client/package.json
index bb80879..224dc1b 100644
--- a/client/package.json
+++ b/client/package.json
@@ -44,6 +44,7 @@
"react-dom": "^15.4.1",
"react-router": "^3.0.2",
"react-scripts": "0.7.0",
+ "react-waypoint": "^5.1.0",
"recursive-readdir": "2.1.0",
"rimraf": "2.5.4",
"strip-ansi": "3.0.1",
diff --git a/client/src/components/PackageSearch/Client.js b/client/src/components/PackageSearch/Client.js
index 29c1ce2..d508728 100644
--- a/client/src/components/PackageSearch/Client.js
+++ b/client/src/components/PackageSearch/Client.js
@@ -1,20 +1,38 @@
import fetch from 'isomorphic-fetch'
function search (query, range, dev, cb) {
- query = encodeURIComponent(query)
- range = encodeURIComponent(range)
-
- return fetch(`/api/query/${query}${range ? '/' : ''}${range}${dev ? '?dev=1' : ''}`, {
- accept: 'application/json'
- }).then(checkStatus)
- .then(parseJSON)
- .then(cb)
- .catch(function (err) {
- cb({
- error: true,
- message: err.message
+ return (limit = 50, gt) => {
+ const _query = encodeURIComponent(query)
+ const _range = encodeURIComponent(range)
+
+ return fetch(withParamteres(`/api/query/${_query}${_range ? '/' : ''}${_range}`, [
+ { name: 'dev', value: 1, condition: dev },
+ { name: 'limit', value: limit },
+ { name: 'gt', value: gt }
+ ]), {
+ accept: 'application/json'
+ }).then(checkStatus)
+ .then(parseJSON)
+ .then(cb)
+ .catch(function (err) {
+ cb({
+ error: true,
+ message: err.message
+ })
})
- })
+ }
+}
+
+function withParamteres (path, parameters) {
+ const condition = (val) => typeof val.condition === 'undefined'
+ ? typeof val.value !== 'undefined'
+ : val.condition
+
+ return parameters.reduce((acc, val) =>
+ condition(val)
+ ? `${acc}${(acc.indexOf('?') === -1) ? '?' : '&'}${val.name}=${val.value}`
+ : acc
+ , path)
}
function checkStatus (response) {
diff --git a/client/src/components/PackageSearch/index.js b/client/src/components/PackageSearch/index.js
index c7fedaf..1497c00 100644
--- a/client/src/components/PackageSearch/index.js
+++ b/client/src/components/PackageSearch/index.js
@@ -5,6 +5,8 @@ import ReactDOM from 'react-dom'
import classnames from 'classnames'
import SearchInfo from '../SearchInfo'
+import SearchResults from '../SearchResults'
+import Waypoint from 'react-waypoint'
import './style.css'
@@ -15,6 +17,17 @@ const resetState = {
}
const PackageSearch = React.createClass({
+
+ propTypes: {
+ limit: React.PropTypes.number
+ },
+
+ getDefaultProps () {
+ return {
+ limit: 50
+ }
+ },
+
getResetState (name, range, dev) {
let state = Object.assign({}, resetState, {})
@@ -118,6 +131,10 @@ const PackageSearch = React.createClass({
let _name = name
+ const res = (result) => [...(this.state.results||[]), ...result]
+
+ const gt = (result) => result[result.length - 1] && result[result.length - 1].name
+
Client.search(name, range, dev, (result) => {
if (result.error) {
let errorState = Object.assign({}, resetState, {
@@ -127,12 +144,13 @@ const PackageSearch = React.createClass({
this.setState(errorState)
} else {
this.setState({
- results: result,
+ results: res(result),
queryName: _name,
- isLoading: false
+ isLoading: false,
+ gt: gt(result)
})
}
- })
+ })(this.props.limit, this.state.gt)
},
componentWillReceiveProps (nextProps) {
@@ -143,6 +161,8 @@ const PackageSearch = React.createClass({
let newState = this.getResetState(packageParam, versionParam, devParam)
+ newState.gt = undefined
+
this.setState(newState, () => {
this.runSearch(packageParam, versionParam, devParam)
})
@@ -201,6 +221,18 @@ const PackageSearch = React.createClass({
)
},
+ onEndOfPage (event) {
+ if (event.event == null) {
+ // waypoint is within viewport
+ return false
+ }
+
+ const resultsLength = this.state.results ? this.state.results.length : 0
+ if (resultsLength % this.props.limit === 0) {
+ this.runSearch()
+ }
+ },
+
render () {
const { searchValueForName, searchValueForRange } = this.state
const showClearIconForName = searchValueForName.length > 0
@@ -305,126 +337,10 @@ const PackageSearch = React.createClass({
) : null}
- "{ errorMessage }" -
- - ) - } - - if (results === null) { - return ( -- { ' '.replace(/ /g, '\u00a0') } -
- ) - } else { - return ( -- Found {results} dependents: -
- ) - } -} - -class SearchResultsModules extends React.Component { - shouldComponentUpdate (nextProps) { - return ( - nextProps.queryName !== this.props.queryName - ) - } - - render () { - const { - results, - queryName - } = this.props - - return ( -Dependent package name | -Latest dependent version | -Range for { queryName } | -
---|---|---|
- - {_package.name} - - | -{_package.version} | -{_package.range} | -
+ "{ errorMessage }" +
+ + ) + } + + if (results === null) { + return ( ++ { ' '.replace(/ /g, '\u00a0') } +
+ ) + } else { + return ( ++ Found {results} dependents. +
+ ) + } +} + +class SearchResultsModules extends React.Component { + shouldComponentUpdate (nextProps) { + return ( + nextProps.queryName !== this.props.queryName || this.props.results.length < nextProps.results.length + ) + } + + render () { + const { + results, + queryName + } = this.props + + return ( +Dependent package name | +Latest dependent version | +Range for { queryName } | +
---|---|---|
+ + {_package.name} + + | +{_package.version} | +{_package.range} | +