diff --git a/ScrollPagination.js b/ScrollPagination.js index bae3cd2..4f93427 100644 --- a/ScrollPagination.js +++ b/ScrollPagination.js @@ -18,6 +18,15 @@ var findScrollParent = function (el) { var ScrollPagination = window.ScrollPagination = React.createClass({ displayName: "ScrollPagination", + getInitialState: function () { + return { + hasPrevPage: false, + hasNextPage: false, + prevPagesMargin: 0, + nextPagesMargin: 0 + }; + }, + getDefaultProps: function () { return { hasPrevPage: false, @@ -30,6 +39,8 @@ var ScrollPagination = window.ScrollPagination = React.createClass({ componentWillMount: function () { this.__pages = {}; this.__pageIds = []; + + this.__initPageExistance(this.props); }, componentDidMount: function () { @@ -37,10 +48,16 @@ var ScrollPagination = window.ScrollPagination = React.createClass({ scrollParent.addEventListener("scroll", this.__handleScroll, false); scrollParent.addEventListener("resize", this.__handleResize, false); + this.__mounted = true; + this.__updatePageIds(); this.__updateDimensions(); }, + componentWillReceiveProps: function (nextProps) { + this.__initPageExistance(nextProps); + }, + componentDidUpdate: function () { this.__updatePageIds(); this.__updateDimensions(); @@ -60,8 +77,11 @@ var ScrollPagination = window.ScrollPagination = React.createClass({ render: function () { var style = {}; - if (this.__paddingTop) { - style.paddingTop = this.__paddingTop + "px"; + if (this.state.prevPagesMargin) { + style.paddingTop = this.state.prevPagesMargin + "px"; + } + if (this.state.nextPagesMargin) { + style.paddingBottom = this.state.nextPagesMargin + "px"; } return React.DOM.div({ style: style, @@ -71,26 +91,67 @@ var ScrollPagination = window.ScrollPagination = React.createClass({ // called from Page component via parent component handlePageEvent: function (pageId, event) { + if (event.name === "mount" || event.name === "update") { + this.__pages[pageId] = { + id: pageId, + height: event.height + }; + + this.__updateMargins(); + } + }, + + __initPageExistance: function (props) { + var hasPrevPage = false; + if (Number.isInteger(props.prevPageCount) && props.prevPageCount > 0) { + hasPrevPage = true; + } else { + hasPrevPage = props.hasPrevPage; + } + + var hasNextPage = false; + if (Number.isInteger(props.nextPageCount) && props.nextPageCount > 0) { + hasNextPage = true; + } else { + hasNextPage = props.hasNextPage; + } + + this.setState({ + hasPrevPage: hasPrevPage, + hasNextPage: hasNextPage + }); + }, + + __updateMargins: function () { + if ( !this.__mounted ) { + return; + } + + var avgHeight = 0; + var count = 0; + var sum = 0; var pages = this.__pages; - switch (event.name) { - case "mount": - pages[pageId] = { - id: pageId, - height: event.height - }; - break; - - case "update": - pages[pageId] = { - id: pageId, - height: event.height - }; - break; - - case "unmount": - break; + Object.keys(pages).forEach(function (pageId) { + count++; + sum += pages[pageId].height; + }); + avgHeight = Math.round(sum / count); + + var prevPagesMargin = 0; + var nextPagesMargin = 0; + + if (Number.isInteger(this.props.prevPageCount)) { + prevPagesMargin = this.props.prevPageCount * avgHeight; } - this.__pages = pages; + + if (Number.isInteger(this.props.nextPageCount)) { + nextPagesMargin = this.props.nextPageCount * avgHeight; + } + + this.setState({ + prevPagesMargin: prevPagesMargin, + nextPagesMargin: nextPagesMargin + }); }, __unloadPage: function (pageId) { @@ -102,7 +163,7 @@ var ScrollPagination = window.ScrollPagination = React.createClass({ }, __loadPrevPage: function () { - if (this.__loadingPrevPage || !this.props.hasPrevPage) { + if (this.__loadingPrevPage || !this.state.hasPrevPage) { return; } this.__loadingPrevPage = true; @@ -110,7 +171,7 @@ var ScrollPagination = window.ScrollPagination = React.createClass({ }, __loadNextPage: function () { - if (this.__loadingNextPage || !this.props.hasNextPage) { + if (this.__loadingNextPage || !this.state.hasNextPage) { return; } this.__loadingNextPage = true; @@ -218,7 +279,7 @@ var ScrollPagination = window.ScrollPagination = React.createClass({ if (this.__dimentions.contentHeight === 0) { return; } - if (this.__loadingNextPage || this.__loadingPrevPage || this.__unloadingPage || !(this.props.hasNextPage || this.props.hasPrevPage)) { + if (this.__loadingNextPage || this.__loadingPrevPage || this.__unloadingPage || !(this.state.hasNextPage || this.state.hasPrevPage)) { if (e) { e.preventDefault(); } @@ -245,17 +306,29 @@ var ScrollPagination = window.ScrollPagination = React.createClass({ var remainingScrollBottom = contentHeight - scrollY - viewportHeight + offsetTop; var remainingScrollTop = contentHeight - remainingScrollBottom - viewportHeight; - if (lastPage && remainingScrollBottom < (lastPage.height / 3)) { - if (secondPage && remainingScrollTop > (firstPage.height + secondPage.height)) { - this.__unloadPage(firstPage.id); - } else { - this.__loadNextPage(); + if (Number.isInteger(this.props.nextPageCount)) { + console.log("TODO: calculate current page from scroll position"); + } else { + if (lastPage && remainingScrollBottom < (lastPage.height / 3)) { + if (secondPage && remainingScrollTop > (firstPage.height + secondPage.height)) { + this.__unloadPage(firstPage.id); + } else { + this.__loadNextPage(); + } + return; } - } else if (firstPage && remainingScrollTop < (firstPage.height / 3)) { - if (secondLastPage && remainingScrollBottom > (lastPage.height + secondLastPage.height)) { - this.__unloadPage(lastPage.id); - } else { - this.__loadPrevPage(); + } + + if (Number.isInteger(this.props.prevPageCount)) { + console.log("TODO: calculate current page from scroll position"); + } else { + if (firstPage && remainingScrollTop < (firstPage.height / 3)) { + if (secondLastPage && remainingScrollBottom > (lastPage.height + secondLastPage.height)) { + this.__unloadPage(lastPage.id); + } else { + this.__loadPrevPage(); + } + return; } } }, @@ -290,7 +363,13 @@ ScrollPagination.Page = React.createClass({ }, render: function () { - return this.props.component(null, this.props.children); + var props = {}; + for (var k in this.props) { + if (k !== "component" && k !== "onPageEvent" && k !== "id" && this.props.hasOwnProperty(k)) { + props[k] = this.props[k]; + } + } + return this.props.component(props, this.props.children); }, __determineHeight: function () { diff --git a/example/index.js b/example/index.js index b7b1369..fc08c96 100644 --- a/example/index.js +++ b/example/index.js @@ -13,8 +13,8 @@ loadNextPage: this.props.loadNextPage, loadPrevPage: this.props.loadPrevPage, unloadPage: this.props.unloadPage, - hasNextPage: this.props.hasNextPage, - hasPrevPage: this.props.hasPrevPage, + nextPageCount: this.props.nextPageCount, + prevPageCount: this.props.prevPageCount }, this.props.pages.map(function (page, index) { return Page({ key: page.id, id: page.id, onPageEvent: this.__handlePageEvent }, page.items.map(function (item) { return React.DOM.div({ key: item.id, style: { paddingTop: index + "px" } }, item.text); @@ -61,6 +61,22 @@ return true; }; + var nextPageCount = function () { + if (loadedPages.length === 0) { + return pages.length; + } + var lastIndex = pages.indexOf(loadedPages[loadedPages.length-1]); + return pages.length - lastIndex; + }; + + var prevPageCount = function () { + if (loadedPages.length === 0) { + return 0; + } + var firstIndex = pages.indexOf(loadedPages[0]); + return firstIndex; + }; + var loadNextPage = function () { setTimeout(function () { var lastLoadedPage = loadedPages[loadedPages.length-1]; @@ -74,7 +90,7 @@ view.setProps({ pages: loadedPages, - hasNextPage: hasNextPage() + nextPageCount: nextPageCount(), }); }, 0); }; @@ -92,7 +108,7 @@ view.setProps({ pages: loadedPages, - hasPrevPage: hasPrevPage() + prevPageCount: prevPageCount() }); }, 0); }; @@ -110,14 +126,14 @@ } if (page === null) { - throw new Error("Invalid attempt to unload page: "+ pageId +"\n"+ JSON.stringify(loadedPages.map(function (p) { return p.id; }))); + // throw new Error("Invalid attempt to unload page: "+ pageId +"\n"+ JSON.stringify(loadedPages.map(function (p) { return p.id; }))); + } else { + loadedPages = loadedPages.slice(0, index).concat(loadedPages.slice(index+1, loadedPages.length)); } - - loadedPages = loadedPages.slice(0, index).concat(loadedPages.slice(index+1, loadedPages.length)); view.setProps({ pages: loadedPages, - hasNextPage: hasNextPage(), - hasPrevPage: hasPrevPage() + nextPageCount: nextPageCount(), + prevPageCount: prevPageCount() }); }, 0); }; @@ -128,8 +144,8 @@ loadNextPage: loadNextPage, loadPrevPage: loadPrevPage, unloadPage: unloadPage, - hasNextPage: true, - hasPrevPage: false + nextPageCount: nextPageCount(), + prevPageCount: prevPageCount() }), el); loadNextPage();