// noPage import { LibComponent } from 'esoftplay/cache/lib/component/import'; import { LibFocus } from 'esoftplay/cache/lib/focus/import'; import React from 'react'; import { Platform, ScrollView, StyleSheet, Text, View, ViewStyle } from 'react-native'; export interface LibCarrouselProps { children: any, autoplay?: boolean, delay?: number, currentPage?: number, style?: ViewStyle, pageStyle?: ViewStyle, contentContainerStyle?: ViewStyle, pageInfo?: boolean, pageInfoBackgroundColor?: string, pageInfoTextStyle?: any, pageInfoBottomContainerStyle?: ViewStyle, pageInfoTextSeparator?: string, bullets?: boolean, bulletsContainerStyle?: ViewStyle, bulletStyle?: ViewStyle, arrows?: boolean, arrowsContainerStyle?: ViewStyle, arrowStyle?: ViewStyle, leftArrowStyle?: ViewStyle, rightArrowStyle?: ViewStyle, leftArrowText?: string, rightArrowText?: string, chosenBulletStyle?: ViewStyle, onAnimateNextPage?: (currentPage: number) => void, onPageBeingChanged?: (nextPage: number) => void, swipe?: boolean, isLooped?: boolean, } export interface LibCarrouselState { currentPage: number, size: any, childrenLength: number, } /** Klik [disini](https://github.com/dev-esoftplay/mobile-docs/blob/main/modules/lib/carrousel.md) untuk melihat dokumentasi*/ export default class m extends LibComponent { static defaultProps = { delay: 4000, autoplay: true, pageInfo: false, bullets: false, arrows: false, pageInfoBackgroundColor: 'rgba(0, 0, 0, 0.25)', pageInfoTextSeparator: ' / ', currentPage: 0, style: undefined, pageStyle: undefined, contentContainerStyle: undefined, pageInfoTextStyle: undefined, pageInfoBottomContainerStyle: undefined, bulletsContainerStyle: undefined, chosenBulletStyle: undefined, bulletStyle: undefined, arrowsContainerStyle: undefined, arrowStyle: undefined, leftArrowStyle: undefined, rightArrowStyle: undefined, leftArrowText: '', rightArrowText: '', onAnimateNextPage: undefined, onPageBeingChanged: undefined, swipe: true, isLooped: true, }; isBackground: boolean = false offset: number = 0; timer: any; nextPage: number = 0; scrollView = React.createRef() constructor(props: LibCarrouselProps) { super(props); const size = { width: 0, height: 0 }; if (props.children) { const childrenLength = React.Children.count(props.children) || 1; this.state = { currentPage: props.currentPage || 0, size, childrenLength, }; } else { this.state = { size, currentPage: 0, childrenLength: 0 }; } this._animateNextPage = this._animateNextPage.bind(this); this._animatePreviousPage = this._animatePreviousPage.bind(this); this._calculateCurrentPage = this._calculateCurrentPage.bind(this); this._calculateNextPage = this._calculateNextPage.bind(this); this._clearTimer = this._clearTimer.bind(this); this._normalizePageNumber = this._normalizePageNumber.bind(this); this._onLayout = this._onLayout.bind(this); this._onScroll = this._onScroll.bind(this); this._onScrollBegin = this._onScrollBegin.bind(this); this._onScrollEnd = this._onScrollEnd.bind(this); this._placeCritical = this._placeCritical.bind(this); this._renderArrows = this._renderArrows.bind(this); this._renderBullets = this._renderBullets.bind(this); this._renderPageInfo = this._renderPageInfo.bind(this); this._scrollTo = this._scrollTo.bind(this); this._setCurrentPage = this._setCurrentPage.bind(this); this._setUpPages = this._setUpPages.bind(this); this._setUpTimer = this._setUpTimer.bind(this); this.animateToPage = this.animateToPage.bind(this); this.getCurrentPage = this.getCurrentPage.bind(this); } componentDidMount(): void { super.componentDidMount(); if (this.state.childrenLength) { this._setUpTimer(); } } componentDidUpdate(prevProps: LibCarrouselProps, prevState: LibCarrouselState): void { const { children } = prevProps const childrenLength = React.Children.count(this.props.children) || 1; const oldChildrenLength = React.Children.count(children) || 1; if (childrenLength != oldChildrenLength) { const { currentPage } = this.state; this._clearTimer(); const nextPage = currentPage >= childrenLength ? childrenLength - 1 : currentPage; if (React.Children.count(children) != React.Children.count(this.props.children)) this.setState({ childrenLength }, () => { this.animateToPage(nextPage); this._setUpTimer(); }); } } componentWillUnmount(): void { super.componentWillUnmount() this._clearTimer(); } _setUpPages(): any { const { size } = this.state; const { children: propsChildren, isLooped, pageStyle } = this.props; const children = React.Children.toArray(propsChildren); const pages: any[] = []; if (children && children.length > 1) { pages.push(...children); if (isLooped) { pages.push(children[0]); pages.push(children[1]); } } else if (children) { pages.push(children[0]); } else { pages.push(You are supposed to add children inside Carousel); } return pages.map((page, i) => ( {page} )); } getCurrentPage(): number { return this.state.currentPage; } _setCurrentPage(currentPage: number): void { this.setState({ currentPage }, () => { if (this.props.onAnimateNextPage) { this.props.onAnimateNextPage(currentPage); } }); } _onScrollBegin(): void { this._clearTimer(); } _onScrollEnd(event: any): void { if (this.state.childrenLength > 1) { const offset = { ...event.nativeEvent.contentOffset }; const page = this._calculateCurrentPage(offset.x); const originalPage = Math.round(offset.x / this.state.size.width); if (originalPage != page) this._placeCritical(page); this._setCurrentPage(page); this._setUpTimer(); } } _onScroll(event: any): void { const currentOffset = event.nativeEvent.contentOffset.x; const direction = currentOffset > this.offset ? 'right' : 'left'; this.offset = currentOffset; const nextPage = this._calculateNextPage(direction); if (this.nextPage !== nextPage) { this.nextPage = nextPage; if (this.props.onPageBeingChanged) { this.props.onPageBeingChanged(this.nextPage); } } } _onLayout(event: any): void { const { height, width } = event.nativeEvent.layout; this.setState({ size: { width, height } }); const timer = setTimeout(() => { this._placeCritical(this.state.currentPage) clearTimeout(timer) }, 0); } _clearTimer(): void { clearTimeout(this.timer); } _setUpTimer(): void { if (this.props.autoplay && !this.isBackground && React.Children.count(this.props.children) > 1) { this._clearTimer(); this.timer = setTimeout(this._animateNextPage, this.props.delay); } } _scrollTo({ offset, animated, nofix }: any): void { if (typeof this.scrollView?.current?.scrollTo == 'function') { this.scrollView.current?.scrollTo({ y: 0, x: offset, animated }); if (!nofix && Platform.OS === 'android' && !animated) { this.scrollView.current?.scrollTo({ y: 0, x: offset, animated: true }); } } } _animateNextPage(): void { const { currentPage } = this.state; const nextPage = this._normalizePageNumber(currentPage + 1); if (!this.props.isLooped && nextPage < currentPage) { return; } this.animateToPage(nextPage); } _animatePreviousPage(): void { const { currentPage } = this.state; const nextPage = this._normalizePageNumber(currentPage - 1); if (!this.props.isLooped && nextPage > currentPage) { return; } this.animateToPage(nextPage); } animateToPage(page: number): void { const { currentPage, childrenLength, size: { width } } = this.state; const { isLooped } = this.props; const nextPage = this._normalizePageNumber(page); this._clearTimer(); if (nextPage === currentPage) { } else if (nextPage === 0) { if (isLooped) { if (currentPage !== childrenLength - 1) { this._scrollTo({ offset: (childrenLength + 2) * width, animated: false, nofix: true, }); } this._scrollTo({ offset: childrenLength * width, animated: true }); } else { this._scrollTo({ offset: 0, animated: true }); } } else if (nextPage === 1) { if (currentPage === 0 && isLooped) { this._scrollTo({ offset: 0, animated: false, nofix: true }); } this._scrollTo({ offset: width, animated: true }); } else { if (currentPage === 0 && nextPage !== childrenLength - 1) { this._scrollTo({ offset: 0, animated: false, nofix: true }); } this._scrollTo({ offset: nextPage * width, animated: true }); } this._setCurrentPage(nextPage); this._setUpTimer(); } _placeCritical(page: number): void { const { isLooped } = this.props; const { childrenLength, size: { width } } = this.state; let offset = 0; if (page < childrenLength) { if (page === 0 && isLooped) { offset = childrenLength * width; } else { offset = page * width; } } this._scrollTo({ offset, animated: false }); } _normalizePageNumber(page: number): number { const { childrenLength } = this.state; if (page === childrenLength) { return 0; } else if (page > childrenLength) { return 1; } else if (page < 0) { return childrenLength - 1; } return page; } _calculateCurrentPage(offset: number): number { const { width } = this.state.size; const page = Math.round(offset / width); return this._normalizePageNumber(page); } _calculateNextPage(direction: string): number { const { width } = this.state.size; const ratio = this.offset / width; const page = direction === 'right' ? Math.ceil(ratio) : Math.floor(ratio); return this._normalizePageNumber(page); } _renderPageInfo(pageLength: number): any { return ( {`${this.state.currentPage + 1}${this.props.pageInfoTextSeparator}${pageLength}`} ) } _renderBullets(pageLength: number): any { const bullets: any[] = []; for (let i = 0; i < pageLength; i += 1) { bullets.push( this.animateToPage(i)} key={`bullet${i}`}> ); } return ( {bullets} ); } _renderArrows(childrenLength: number): any { let { currentPage } = this.state; if (currentPage < 1) { currentPage = childrenLength; } return ( {this.props.leftArrowText ? this.props.leftArrowText : 'Left'} {this.props.rightArrowText ? this.props.rightArrowText : 'Right'} ); } render(): any { const contents = this._setUpPages(); const containerProps = { onLayout: this._onLayout, style: [this.props.style], }; const { size, childrenLength } = this.state; return ( { this.isBackground = false; this._setUpTimer(); }} onBlur={() => { this.isBackground = true; this._clearTimer(); }} /> 1 && this.props.isLooped ? 2 : 0)), height: size.height, }, ]} > {contents} {this.props.arrows && this._renderArrows(this.state.childrenLength)} {this.props.bullets && this._renderBullets(this.state.childrenLength)} {this.props.pageInfo && this._renderPageInfo(this.state.childrenLength)} ); } } const styles = StyleSheet.create({ horizontalScroll: { position: 'absolute', }, pageInfoBottomContainer: { position: 'absolute', bottom: 20, left: 0, right: 0, backgroundColor: 'transparent', }, pageInfoContainer: { alignItems: 'center', justifyContent: 'center', backgroundColor: 'transparent', }, pageInfoPill: { width: 80, height: 20, borderRadius: 10, alignItems: 'center', justifyContent: 'center', }, pageInfoText: { textAlign: 'center', }, bullets: { position: 'absolute', left: 0, right: 0, bottom: 10, height: 30, backgroundColor: 'transparent', alignItems: 'center', justifyContent: 'center', flexDirection: 'row', }, arrows: { position: 'absolute', left: 0, right: 0, bottom: 0, top: 0, backgroundColor: 'transparent', }, arrowsContainer: { flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', }, chosenBullet: { margin: 10, width: 10, height: 10, borderRadius: 20, backgroundColor: 'white', }, bullet: { margin: 10, width: 10, height: 10, borderRadius: 20, backgroundColor: 'transparent', borderColor: 'white', borderWidth: 1, }, });