import * as React from "react"; import { Dimensions, EmitterSubscription, LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, ScrollView, View, } from "react-native"; import BaseScrollComponent, { ScrollComponentProps } from "../../../core/scrollcomponent/BaseScrollComponent"; import TSCast from "../../../utils/TSCast"; import debounce = require("lodash.debounce"); /*** * The responsibility of a scroll component is to report its size, scroll events and provide a way to scroll to a given offset. * RecyclerListView works on top of this interface and doesn't care about the implementation. To support web we only had to provide * another component written on top of web elements */ export default class ScrollComponent extends BaseScrollComponent { public static defaultProps: Partial = { contentHeight: 0, contentWidth: 0, externalScrollView: TSCast.cast(ScrollView), isHorizontal: false, scrollThrottle: 16, }; private static _defaultContainer(props: object, children: React.ReactNode): React.ReactNode | null { return ( {children} ); } private _height: number; private _width: number; private _offset: number; private _isSizeChangedCalledOnce: boolean; private _scrollViewRef: ScrollView | null = null; private _dimensionsChangeSubscription: EmitterSubscription | null = null; constructor(args: ScrollComponentProps) { super(args); this._height = (args.layoutSize && args.layoutSize.height) || 0; this._width = (args.layoutSize && args.layoutSize.width) || 0; this._offset = 0; this._isSizeChangedCalledOnce = false; } public componentDidMount(): void { this._dimensionsChangeSubscription = Dimensions.addEventListener("change", debounce(({ window }: any) => { this.props.onWindowResize({ width: window.width, height: window.height, }); }, 100)); } public componentWillUnmount(): void { if (this._dimensionsChangeSubscription) { this._dimensionsChangeSubscription.remove(); } } public scrollTo(x: number, y: number, isAnimated: boolean): void { if (this._scrollViewRef) { this._scrollViewRef.scrollTo({ x, y, animated: isAnimated }); } } public getScrollableNode(): number | null { if (this._scrollViewRef && this._scrollViewRef.getScrollableNode) { return this._scrollViewRef.getScrollableNode(); } return null; } public getNativeScrollRef(): ScrollView | null { return this._scrollViewRef; } public render(): JSX.Element { const Scroller = TSCast.cast(this.props.externalScrollView); //TSI const renderContentContainer = this.props.renderContentContainer ? this.props.renderContentContainer : ScrollComponent._defaultContainer; const contentContainerProps = { style: { height: this.props.contentHeight, width: this.props.contentWidth, }, horizontal: this.props.isHorizontal, scrollOffset: this._offset, renderAheadOffset: this.props.renderAheadOffset, windowSize: (this.props.isHorizontal ? this._width : this._height) + this.props.renderAheadOffset, }; //TODO:Talha // const { // useWindowScroll, // contentHeight, // contentWidth, // externalScrollView, // canChangeSize, // renderFooter, // isHorizontal, // scrollThrottle, // ...props, // } = this.props; return ( // @ts-ignore {this.props.renderHeader && this.props.renderHeader()} {renderContentContainer(contentContainerProps, this.props.children)} {this.props.renderFooter && this.props.renderFooter()} ); } private _defaultContainer(props: object, children: React.ReactNode): React.ReactNode | null { return ( {children} ); } private _getScrollViewRef = (scrollView: any) => { this._scrollViewRef = scrollView as (ScrollView | null); }; private _onScroll = (event?: any): void => { if (event) { const contentOffset = event.nativeEvent.contentOffset; this._offset = this.props.isHorizontal ? contentOffset.x : contentOffset.y; this.props.onScroll(contentOffset.x, contentOffset.y, event); } } private _onLayout = (event: any): void => { if (this._height !== event.nativeEvent.layout.height || this._width !== event.nativeEvent.layout.width) { this._height = event.nativeEvent.layout.height; this._width = event.nativeEvent.layout.width; if (this.props.onSizeChanged) { this._isSizeChangedCalledOnce = true; this.props.onSizeChanged(event.nativeEvent.layout); } } if (this.props.onLayout) { this.props.onLayout(event); } } }