diff --git a/.npmignore b/.npmignore index 13ae3c7c..d17cf412 100644 --- a/.npmignore +++ b/.npmignore @@ -1,6 +1,6 @@ node_modules/ samples/ -scripts/ +#scripts/ .idea/ .babelrc .travis.yml diff --git a/package.json b/package.json index 5ecb4fa3..082ecfda 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "dependencies": { "lodash.debounce": "4.0.8", "prop-types": "15.5.8", + "react-alert": "^4.0.2", "ts-object-utils": "0.0.5" }, "peerDependencies": { diff --git a/src/core/RecyclerListView.tsx b/src/core/RecyclerListView.tsx index 2ebb043a..fffa0167 100644 --- a/src/core/RecyclerListView.tsx +++ b/src/core/RecyclerListView.tsx @@ -64,8 +64,9 @@ const refreshRequestDebouncer = debounce((executable: () => void) => { /*** * This is the main component, please refer to samples to understand how to use. * For advanced usage check out prop descriptions below. - * You also get common methods such as: scrollToIndex, scrollToItem, scrollToTop, scrollToEnd, scrollToOffset, getCurrentScrollOffset, - * findApproxFirstVisibleIndex. + * You also get common methods such as: scrollToApproxFirstVisibleIndex, scrollToApproxLastVisibleIndex, scrollToApproxMiddleVisibleIndex, + * scrollToIndex, scrollToItem, scrollToTop, scrollToEnd, scrollToOffset, getCurrentScrollOffset, findApproxFirstVisibleIndex, findApproxLastVisibleIndex, + * findApproxMiddleVisibleIndex. * You'll need a ref to Recycler in order to call these * Needs to have bounded size in all cases other than window scrolling (web). * @@ -94,7 +95,7 @@ export interface RecyclerListViewProps { onEndReachedThreshold?: number; onVisibleIndexesChanged?: TOnItemStatusChanged; renderFooter?: () => JSX.Element | JSX.Element[] | null; - externalScrollView?: { new(props: ScrollViewDefaultProps): BaseScrollView }; + externalScrollView?: new(props: ScrollViewDefaultProps) => BaseScrollView; initialOffset?: number; initialRenderIndex?: number; scrollThrottle?: number; @@ -270,6 +271,24 @@ export default class RecyclerListView

{ if (this._scrollComponent) { if (this.props.isHorizontal) { @@ -313,6 +332,16 @@ export default class RecyclerListView

= 0; i--) { if (this._isHorizontal) { @@ -123,6 +122,24 @@ export default class ViewabilityTracker { return result; } + public findFirstLogicallyVisibleIndex(): number { + const relevantIndex = this._findFirstVisibleIndexUsingBS(0.001); + const result = this.findLogicallyVisibleIndex(relevantIndex); + return result; + } + + public findLastLogicallyVisibleIndex(): number { + const relevantIndex = this._findLastVisibleIndexUsingBS(0.001); + const result = this.findLogicallyVisibleIndex(relevantIndex); + return result; + } + + public findMiddleLogicallyVisibleIndex(): number { + const relevantIndex = this._findMiddleVisibleIndex(); + const result = this.findLogicallyVisibleIndex(relevantIndex); + return result; + } + public updateRenderAheadOffset(renderAheadOffset: number): void { this._renderAheadOffset = Math.max(0, renderAheadOffset); this.forceRefreshWithOffset(this._currentOffset); @@ -180,6 +197,17 @@ export default class ViewabilityTracker { return BinarySearch.findClosestHigherValueIndex(count, this._visibleWindow.start + bias, this._valueExtractorForBinarySearch); } + private _findLastVisibleIndexUsingBS(bias = 0): number { + const count = this._layouts.length; + return BinarySearch.findClosestHigherValueIndex(count, this._visibleWindow.end - bias, this._valueExtractorForBinarySearch); + } + + private _findMiddleVisibleIndex(): number { + const count = this._layouts.length; + const targetValue = Math.round( (this._visibleWindow.start + this._visibleWindow.end) / 2 ); + return BinarySearch.findClosestHigherValueIndex(count, targetValue, this._valueExtractorForBinarySearch); + } + private _valueExtractorForBinarySearch = (index: number): number => { const itemRect = this._layouts[index]; this._setRelevantBounds(itemRect, this._relevantDim); diff --git a/src/core/scrollcomponent/BaseScrollComponent.tsx b/src/core/scrollcomponent/BaseScrollComponent.tsx index 658836cf..e7628c31 100644 --- a/src/core/scrollcomponent/BaseScrollComponent.tsx +++ b/src/core/scrollcomponent/BaseScrollComponent.tsx @@ -8,7 +8,7 @@ export interface ScrollComponentProps { contentHeight: number; contentWidth: number; canChangeSize?: boolean; - externalScrollView?: { new(props: ScrollViewDefaultProps): BaseScrollView }; + externalScrollView?: new(props: ScrollViewDefaultProps) => BaseScrollView; isHorizontal?: boolean; renderFooter?: () => JSX.Element | JSX.Element[] | null; scrollThrottle?: number; diff --git a/src/platform/web/scrollcomponent/ScrollViewer.tsx b/src/platform/web/scrollcomponent/ScrollViewer.tsx index cb6ec074..2df74e5a 100644 --- a/src/platform/web/scrollcomponent/ScrollViewer.tsx +++ b/src/platform/web/scrollcomponent/ScrollViewer.tsx @@ -144,17 +144,26 @@ export default class ScrollViewer extends BaseScrollView { start = Math.min(offset + 800, start); } const change = offset - start; - const increment = 20; const duration = 200; - const animateScroll = (elapsedTime: number) => { - elapsedTime += increment; + let elapsedTime = 0; + let lastRenderTimestamp: number = -1; + const animateScroll = (timestamp: number) => { + + if ( lastRenderTimestamp === -1 ) { + lastRenderTimestamp = timestamp - 20; + } + + const deltaTime = timestamp - lastRenderTimestamp; + lastRenderTimestamp = timestamp; + elapsedTime = Math.min ( elapsedTime + deltaTime, duration ); + const position = this._easeInOut(elapsedTime, start, change, duration); this._setRelevantOffset(position); if (elapsedTime < duration) { - window.setTimeout(() => animateScroll(elapsedTime), increment); + window.requestAnimationFrame(animateScroll); } }; - animateScroll(0); + window.requestAnimationFrame(animateScroll); } private _startListeningToDivEvents(): void { diff --git a/tsconfig.json b/tsconfig.json index c9400d72..8a73b92c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,6 +12,8 @@ "removeComments": false, "jsx": "react", "sourceMap": true, - "skipLibCheck": true + "skipLibCheck": true, + "strictNullChecks": false, + "noImplicitAny": false } }