UNPKG

react-native-image-layout

Version:

An easy and simple to use React Native component to render a custom masonry layout for remote/local images and displayed on a custom interactive image viewer. Includes animations and support for both iOS and Android. Free and made possible along with co

240 lines (225 loc) 8.81 kB
import React from "react"; import PropTypes from "prop-types"; import { Modal, Platform, View } from "react-native"; import MasonryList from "react-native-masonry-list"; import ImageViewer from "./ImageViewer"; class ImageLayout extends React.PureComponent { static propTypes = { images: PropTypes.arrayOf( PropTypes.object.isRequired ).isRequired, onEndReached: PropTypes.func, onEndReachedThreshold: PropTypes.number, // Masonry props columns: PropTypes.number, spacing: PropTypes.number, initialColToRender: PropTypes.number, initialNumInColsToRender: PropTypes.number, sorted: PropTypes.bool, masonryFlatListColProps: PropTypes.object, renderMainHeader: PropTypes.func, renderMainFooter: PropTypes.func, onLongPressImage: PropTypes.func, imageContainerStyle: PropTypes.object, renderIndividualMasonryHeader: PropTypes.func, renderIndividualMasonryFooter: PropTypes.func, rerender: PropTypes.bool, // Gallery props imagePageComponent: PropTypes.func, errorPageComponent: PropTypes.func, pagesFlatListProps: PropTypes.object, pageMargin: PropTypes.number, sensitivePageScroll: PropTypes.bool, onPageSelected: PropTypes.func, onPageScrollStateChanged: PropTypes.func, onPageScroll: PropTypes.func, pageScrollViewStyle: PropTypes.object, onPageSingleTapConfirmed: PropTypes.func, onPageLongPress: PropTypes.func, renderPageHeader: PropTypes.func, renderPageFooter: PropTypes.func, onDoubleTapConfirmed: PropTypes.func, onDoubleTapStartReached: PropTypes.func, onDoubleTapEndReached: PropTypes.func, onPinchTransforming: PropTypes.func, onPinchStartReached: PropTypes.func, onPinchEndReached: PropTypes.func, enableScale: PropTypes.bool, enableTranslate: PropTypes.bool, resizeMode: PropTypes.string, enableResistance: PropTypes.bool, resistantStrHorizontal: PropTypes.oneOfType([ PropTypes.func, PropTypes.number, PropTypes.string ]), resistantStrVertical: PropTypes.oneOfType([ PropTypes.func, PropTypes.number, PropTypes.string ]), onViewTransformed: PropTypes.func, onTransformGestureReleased: PropTypes.func, onSwipeUpReleased: PropTypes.func, onSwipeDownReleased: PropTypes.func, maxScale: PropTypes.number, maxOverScrollDistance: PropTypes.number, enableVerticalExit: PropTypes.bool, enableModal: PropTypes.bool, } static defaultProps = { images: [], columns: 2, spacing: 1, initialColToRender: null, initialNumInColsToRender: 1, sorted: false, imageContainerStyle: {}, onEndReachedThreshold: 0.8, sensitivePageScroll: false, enableVerticalExit: true, enableModal: false } constructor(props) { super(props); this.state = { resolvedData: [], displayImageViewer: false, galleryInitialIndex: 0, galleryIndex: 0, imageId: "" }; } _setImageData = (data) => { this.setState({ resolvedData: data }); } openImageViewer = (imageId, index) => { this.setState({ displayImageViewer: true, imageId, galleryInitialIndex: index }); } onChangePhoto = (imageId, galleryIndex) => { this.setState({ imageId, galleryIndex }); } closeImageViewer = () => { this.setState({ displayImageViewer: false, imageId: "" }); } render() { let Injectant; const injectantProps = {}; if (this.props.enableModal) { Injectant = Modal; injectantProps.visible = this.state.displayImageViewer && this.state.imageId ? true : false; injectantProps.transparent = true; injectantProps.animationType = Platform.OS === "ios" ? "none" : "fade"; injectantProps.hardwareAccelerated = true; injectantProps.onRequestClose = this.closeImageViewer; } else { Injectant = View; injectantProps.style = { position: "absolute" }; } return ( <View style={{flex: 1}} {...this.props}> { this.props.renderMainHeader && this.props.renderMainHeader() } <MasonryList images={this.props.images} columns={this.props.columns} spacing={this.props.spacing} // TODO: add masonry header and footer that // flows with the FlatList // renderMasonryHeader={this.props.renderMasonryHeader} // renderMasonryFooter={this.props.renderMasonryFooter} initialColToRender={this.props.initialColToRender} initialNumInColsToRender={this.props.initialNumInColsToRender} sorted={this.props.sorted} onLongPressImage={this.props.onLongPressImage} imageContainerStyle={this.props.imageContainerStyle} renderIndividualMasonryHeader={this.props.renderIndividualMasonryHeader} renderIndividualMasonryFooter={this.props.renderIndividualMasonryFooter} masonryFlatListColProps={this.props.masonryFlatListColProps} rerender={this.props.rerender} onImageResolved={(resolvedImage) => { resolvedImage.id = Math.random().toString(36).substring(7); return resolvedImage; }} onImagesResolveEnd={(resolvedImages) => { const resolvedData = resolvedImages.reduce((acc, curr) => acc.concat(curr)).sort(function (a, b) { return a.index - b.index; }); this.setState({ resolvedData: resolvedData }); }} onPressImage={(data, i) => this.openImageViewer(data.id, i)} onEndReached={this.props.onEndReached} onEndReachedThreshold={this.props.onEndReachedThreshold} /> { this.props.renderMainFooter && this.props.renderMainFooter() } {this.state.displayImageViewer && this.state.imageId ? ( <Injectant {...injectantProps}> <ImageViewer images={this.state.resolvedData} imageId={this.state.imageId} galleryInitialIndex={this.state.galleryInitialIndex} galleryIndex={this.state.galleryIndex} onClose={this.closeImageViewer} onChangePhoto={this.onChangePhoto} displayImageViewer={this.state.displayImageViewer} imagePageComponent={this.props.imagePageComponent} errorPageComponent={this.props.errorPageComponent} pagesFlatListProps={this.props.pagesFlatListProps} pageMargin={this.props.pageMargin} sensitivePageScroll={this.props.sensitivePageScroll} onPageSelected={this.props.onPageSelected} onPageScrollStateChanged={this.props.onPageScrollStateChanged} onPageScroll={this.props.onPageScroll} pageScrollViewStyle={this.props.pageScrollViewStyle} onPageSingleTapConfirmed={this.props.onPageSingleTapConfirmed} onPageLongPress={this.props.onPageLongPress} renderPageHeader={this.props.renderPageHeader} renderPageFooter={this.props.renderPageFooter} onDoubleTapConfirmed={this.props.onDoubleTapConfirmed} onDoubleTapStartReached={this.props.onDoubleTapStartReached} onDoubleTapEndReached={this.props.onDoubleTapEndReached} onPinchTransforming={this.props.onPinchTransforming} onPinchStartReached={this.props.onPinchStartReached} onPinchEndReached={this.props.onPinchEndReached} enableScale={this.props.enableScale} enableTranslate={this.props.enableTranslate} resizeMode={this.props.resizeMode} enableResistance={this.props.enableResistance} resistantStrHorizontal={this.props.resistantStrHorizontal} resistantStrVertical={this.props.resistantStrVertical} onViewTransformed={this.props.onViewTransformed} onTransformGestureReleased={this.props.onTransformGestureReleased} onSwipeUpReleased={this.props.onSwipeUpReleased} onSwipeDownReleased={this.props.onSwipeDownReleased} maxScale={this.props.maxScale} maxOverScrollDistance={this.props.maxOverScrollDistance} enableVerticalExit={this.props.enableVerticalExit} enableModal={this.props.enableModal} onEndReached={this.props.onEndReached} onEndReachedThreshold={this.props.onEndReachedThreshold} /> </Injectant> ) : null} </View> ); } } export default ImageLayout;