Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@valdio/react-native-scrollable-tabview

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@valdio/react-native-scrollable-tabview - npm Package Compare versions

Comparing version
0.8.9
to
0.8.10
+15
lib/Button.android.js
import React from 'react'
import ReactNative from 'react-native'
const {TouchableNativeFeedback} = ReactNative
export default Button = (props) =>
<TouchableNativeFeedback
delayPressIn={0}
background={TouchableNativeFeedback.SelectableBackground()} // eslint-disable-line new-cap
{...props}
>
{props.children}
</TouchableNativeFeedback>
import React from 'react'
import ReactNative from 'react-native'
const {TouchableOpacity} = ReactNative
export default Button = (props) => {
return <TouchableOpacity {...props}>
{props.children}
</TouchableOpacity>
}
import React from 'react'
import ReactNative from 'react-native'
const {TouchableOpacity} = ReactNative
export default Button = (props) => {
return <TouchableOpacity {...props}>
{props.children}
</TouchableOpacity>
}
import React, {Component} from 'react'
import ReactNative from 'react-native'
import PropTypes from 'prop-types'
const {
StyleSheet,
Text,
View,
ViewPropTypes,
Animated
} = ReactNative
import Button from './Button'
import ScrollableTabView from './index'
const defaultProps = {
activeTextColor: 'navy',
inactiveTextColor: 'black',
backgroundColor: null
}
export default class DefaultTabBar extends React.Component {
constructor(props) {
super(props)
this.renderTab = this.renderTab.bind(this)
}
renderTabOption(name, page) {
}
renderTab(name, page, isTabActive, onPressHandler) {
const {activeTextColor, inactiveTextColor, textStyle} = this.props
const textColor = isTabActive ? activeTextColor : inactiveTextColor
const fontWeight = isTabActive ? 'bold' : 'normal'
return <Button
style={styles.flexOne}
key={name}
accessible={true}
accessibilityLabel={name}
accessibilityTraits='button'
onPress={() => onPressHandler(page)}
>
<View style={[styles.tab, this.props.tabStyle]}>
<Text style={[{color: textColor, fontWeight}, textStyle]}>
{name}
</Text>
</View>
</Button>
}
render() {
const containerWidth = this.props.containerWidth
const numberOfTabs = this.props.tabs.length
const tabUnderlineStyle = {
position: 'absolute',
width: containerWidth / numberOfTabs,
height: 4,
backgroundColor: 'navy',
bottom: 0
}
const left = this.props.scrollValue.interpolate({
inputRange: [0, 1], outputRange: [0, containerWidth / numberOfTabs]
})
return (
<View style={[styles.tabs, {backgroundColor: this.props.backgroundColor}, this.props.style]}>
{this.props.tabs.map((name, page) => {
const isTabActive = this.props.activeTab === page
const renderTab = this.props.renderTab || this.renderTab
return renderTab(name, page, isTabActive, this.props.goToPage) // () =>
})}
<Animated.View style={[tabUnderlineStyle, {left}, this.props.underlineStyle]}/>
</View>
)
}
}
DefaultTabBar.propTypes = {
goToPage: PropTypes.func,
activeTab: PropTypes.number,
tabs: PropTypes.array,
backgroundColor: PropTypes.string,
activeTextColor: PropTypes.string,
inactiveTextColor: PropTypes.string,
textStyle: Text.propTypes.style,
tabStyle: ViewPropTypes.style,
renderTab: PropTypes.func,
underlineStyle: ViewPropTypes.style
}
DefaultTabBar.defaultProps = {
activeTextColor: 'navy',
inactiveTextColor: 'black',
backgroundColor: null
}
const styles = StyleSheet.create({
tab: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingBottom: 10
},
flexOne: {
flex: 1
},
tabs: {
height: 50,
flexDirection: 'row',
justifyContent: 'space-around',
borderWidth: 1,
borderTopWidth: 0,
borderLeftWidth: 0,
borderRightWidth: 0,
borderColor: '#ccc'
}
})
import React, {Component} from 'react'
import ReactNative from 'react-native'
import PropTypes from 'prop-types'
const {
ViewPropTypes,
Dimensions,
View,
Animated,
ScrollView,
StyleSheet,
InteractionManager,
Platform,
RefreshControl
} = ReactNative
// import TimerMixin from 'react-timer-mixin'
import SceneComponent from './SceneComponent'
import DefaultTabBar from './DefaultTabBar'
import ScrollableTabBar from './ScrollableTabBar'
/**
* pullToRefresh: function used to trigger pull to refresh action.
* Function muns have a callback response in order to stop pull to refresh action.
* refreshControlStyle: style of RefreshControl
*/
class ScrollableTabView extends Component {
constructor(props) {
super(props)
this.state = this.getInitialState()
this._handleLayout = this._handleLayout.bind(this)
this.goToPage = this.goToPage.bind(this)
this.renderTabBar = this.renderTabBar.bind(this)
this.updateSceneKeys = this.updateSceneKeys.bind(this)
this.newSceneKeys = this.newSceneKeys.bind(this)
this._shouldRenderSceneKey = this._shouldRenderSceneKey.bind(this)
this._keyExists = this._keyExists.bind(this)
this._makeSceneKey = this._makeSceneKey.bind(this)
this.renderScrollableContent = this.renderScrollableContent.bind(this)
this._composeScenes = this._composeScenes.bind(this)
this._onMomentumScrollBeginAndEnd = this._onMomentumScrollBeginAndEnd.bind(this)
this._updateSelectedPage = this._updateSelectedPage.bind(this)
this.renderCollapsableBar = this.renderCollapsableBar.bind(this)
}
// mixins = [TimerMixin]
/* getDefaultProps() {
return {
tabBarPosition: 'top',
initialPage: 0,
page: -1,
onChangeTab: () => {
},
onScroll: () => {
},
contentProps: {},
scrollWithoutAnimation: false,
locked: false,
prerenderingSiblingsNumber: 0
}
}*/
getInitialState() {
const width = Dimensions.get('window').width
return {
currentPage: this.props.initialPage,
scrollX: new Animated.Value(this.props.initialPage * width),
scrollValue: new Animated.Value(this.props.initialPage),
containerWidth: width,
sceneKeys: this.newSceneKeys({currentPage: this.props.initialPage}),
refreshing: false
}
}
_onRefresh = () => {
//if there is not pullToRefresh function do nothing
if (!this.props.pullToRefresh)
return
this.setState({refreshing: true})
this.props.pullToRefresh(response => {
this.setState({refreshing: false})
})
}
componentDidMount() {
setTimeout(() => {
InteractionManager.runAfterInteractions(() => {
if (Platform.OS === 'android') {
this.goToPage(this.props.initialPage, false)
}
})
}, 0)
this.state.scrollX.addListener(({value}) => {
const scrollValue = value / this.state.containerWidth
this.state.scrollValue.setValue(scrollValue)
this.props.onScroll(scrollValue)
})
}
componentWillReceiveProps(props) {
if (props.children !== this.props.children) {
this.updateSceneKeys({page: this.state.currentPage, children: props.children})
}
if (props.page >= 0 && props.page !== this.state.currentPage) {
this.goToPage(props.page)
}
}
goToPage(pageNumber, animated = !this.props.scrollWithoutAnimation) {
const offset = pageNumber * this.state.containerWidth
if (this.scrollView && this.scrollView._component && this.scrollView._component.scrollTo) {
this.scrollView._component.scrollTo({x: offset, y: 0, animated})
}
const currentPage = this.state.currentPage
this.updateSceneKeys({
page: pageNumber,
callback: this._onChangeTab.bind(this, currentPage, pageNumber)
})
}
renderTabBar(props) {
if (this.props.renderTabBar === false) {
return null
} else if (this.props.renderTabBar) {
return React.cloneElement(this.props.renderTabBar(props), props)
} else {
return <DefaultTabBar {...props} />
}
}
updateSceneKeys({
page, children = this.props.children, callback = () => {
}
}) {
let newKeys = this.newSceneKeys({previousKeys: this.state.sceneKeys, currentPage: page, children})
this.setState({currentPage: page, sceneKeys: newKeys}, callback)
}
newSceneKeys({previousKeys = [], currentPage = 0, children = this.props.children}) {
let newKeys = []
this._children(children).forEach((child, idx) => {
let key = this._makeSceneKey(child, idx)
if (this._keyExists(previousKeys, key) ||
this._shouldRenderSceneKey(idx, currentPage)) {
newKeys.push(key)
}
})
return newKeys
}
_shouldRenderSceneKey(idx, currentPageKey) {
let numOfSibling = this.props.prerenderingSiblingsNumber
return (idx < (currentPageKey + numOfSibling + 1) &&
idx > (currentPageKey - numOfSibling - 1))
}
_keyExists(sceneKeys, key) {
return sceneKeys.find((sceneKey) => key === sceneKey)
}
_makeSceneKey(child, idx) {
return child.props.tabLabel + '_' + idx
}
renderScrollableContent() {
//in case of the collapsible scroll view the pull to refresh animation will be applied on the container
//on the other case the refresh animations will be applied here.
const isContainerScrollView = !!this.props.collapsableBar
const scenes = this._composeScenes()
return <Animated.ScrollView
refreshControl={!isContainerScrollView && this.props.pullToRefresh && typeof this.props.pullToRefresh === 'function' &&
<RefreshControl style={this.props.refreshControlStyle || {}}
refreshing={this.state.refreshing}
onRefresh={this._onRefresh}/> || undefined}
showsVerticalScrollIndicator={this.props.showsVerticalScrollIndicator}
showsHorizontalScrollIndicator={this.props.showsHorizontalScrollIndicator}
horizontal
pagingEnabled
automaticallyAdjustContentInsets={false}
contentOffset={{x: this.props.initialPage * this.state.containerWidth}}
ref={scrollView => this.scrollView = scrollView}
onScroll={Animated.event([{nativeEvent: {contentOffset: {x: this.state.scrollX}}}], {useNativeDriver: true})}
onMomentumScrollBegin={this._onMomentumScrollBeginAndEnd}
onMomentumScrollEnd={this._onMomentumScrollBeginAndEnd}
scrollEventThrottle={16}
scrollsToTop={false}
scrollEnabled={!this.props.locked}
directionalLockEnabled
alwaysBounceVertical={false}
keyboardDismissMode="on-drag"
{...this.props.contentProps}
>
{scenes}
</Animated.ScrollView>
}
_composeScenes() {
return this._children().map((child, idx) => {
let key = this._makeSceneKey(child, idx)
let element
if (!!this.props.collapsableBar) {
element = this.state.currentPage === idx ? child : null
} else {
element = this._keyExists(this.state.sceneKeys, key) ? child : <View tabLabel={child.props.tabLabel}/>
}
return <SceneComponent
key={child.key}
shouldUpdated={!!this.props.collapsableBar || this._shouldRenderSceneKey(idx, this.state.currentPage)}
style={{width: this.state.containerWidth}}
>
{element}
</SceneComponent>
})
}
_onMomentumScrollBeginAndEnd(e) {
const offsetX = e.nativeEvent.contentOffset.x
const page = Math.round(offsetX / this.state.containerWidth)
if (this.state.currentPage !== page) {
this._updateSelectedPage(page)
}
}
_updateSelectedPage(nextPage) {
let localNextPage = nextPage
if (typeof localNextPage === 'object') {
localNextPage = nextPage.nativeEvent.position
}
const currentPage = this.state.currentPage
this.updateSceneKeys({
page: localNextPage,
callback: this._onChangeTab.bind(this, currentPage, localNextPage)
})
}
_onChangeTab(prevPage, currentPage) {
this.props.onChangeTab({
i: currentPage,
ref: this._children()[currentPage],
from: prevPage
})
if (this.contentScrollDistance >= this.collapsableBarHeight) {
this.contentView.scrollTo({x: 0, y: this.collapsableBarHeight, animated: false})
}
}
_handleLayout(e) {
const {width} = e.nativeEvent.layout
if (Math.round(width) !== Math.round(this.state.containerWidth)) {
this.setState({containerWidth: width})
this.requestAnimationFrame(() => {
this.goToPage(this.state.currentPage)
})
}
}
_children(children = this.props.children) {
return React.Children.map(children, (child) => child)
}
renderCollapsableBar() {
if (!this.props.collapsableBar) {
return null
}
return React.cloneElement(this.props.collapsableBar, {
onLayout: event => {
this.collapsableBarHeight = event.nativeEvent.layout.height
}
})
}
render() {
let overlayTabs = (this.props.tabBarPosition === 'overlayTop' || this.props.tabBarPosition === 'overlayBottom')
let tabBarProps = {
goToPage: this.goToPage,
tabs: this._children().map((child) => child.props.tabLabel),
activeTab: this.state.currentPage,
scrollX: this.state.scrollX,
scrollValue: this.state.scrollValue,
containerWidth: this.state.containerWidth
}
if (this.props.tabBarBackgroundColor) {
tabBarProps.backgroundColor = this.props.tabBarBackgroundColor
}
if (this.props.tabBarActiveTextColor) {
tabBarProps.activeTextColor = this.props.tabBarActiveTextColor
}
if (this.props.tabBarInactiveTextColor) {
tabBarProps.inactiveTextColor = this.props.tabBarInactiveTextColor
}
if (this.props.tabBarTextStyle) {
tabBarProps.textStyle = this.props.tabBarTextStyle
}
if (this.props.tabBarUnderlineStyle) {
tabBarProps.underlineStyle = this.props.tabBarUnderlineStyle
}
if (overlayTabs) {
tabBarProps.style = {
position: 'absolute',
left: 0,
right: 0,
[this.props.tabBarPosition === 'overlayTop' ? 'top' : 'bottom']: 0
}
}
const ContainerView = this.props.collapsableBar ? ScrollView : View
const isScrollView = this.props.collapsableBar ? true : false
return (<ContainerView
refreshControl={isScrollView && this.props.pullToRefresh && typeof this.props.pullToRefresh === 'function' &&
<RefreshControl style={this.props.refreshControlStyle || {}}
refreshing={this.state.refreshing}
onRefresh={this._onRefresh}/> || undefined}
showsVerticalScrollIndicator={isScrollView && this.props.showsVerticalScrollIndicator}
showsHorizontalScrollIndicator={isScrollView && this.props.showsHorizontalScrollIndicator}
style={[styles.container, this.props.style]}
onLayout={this._handleLayout} //()=>
ref={contentView => this.contentView = contentView}
onMomentumScrollEnd={event => {
this.contentScrollDistance = event.nativeEvent.contentOffset.y
}}
stickyHeaderIndices={this.props.collapsableBar ? [1] : []}>
<View style={[{flex: 1}, this.props.contentStyle || {}]}>
{this.renderCollapsableBar()}
{this.props.tabBarPosition === 'top' && this.renderTabBar(tabBarProps)}
{this.renderScrollableContent()}
{(this.props.tabBarPosition === 'bottom' || overlayTabs) && this.renderTabBar(tabBarProps)}
</View>
</ContainerView>
)
}
}
ScrollableTabView.defaultProps = {
tabBarPosition: 'top',
initialPage: 0,
page: -1,
onChangeTab: () => {
},
onScroll: () => {
},
contentProps: {},
scrollWithoutAnimation: false,
locked: false,
prerenderingSiblingsNumber: 0
}
ScrollableTabView.propTypes = {
tabBarPosition: PropTypes.oneOf(['top', 'bottom', 'overlayTop', 'overlayBottom']),
initialPage: PropTypes.number,
page: PropTypes.number,
onChangeTab: PropTypes.func,
onScroll: PropTypes.func,
renderTabBar: PropTypes.any,
style: ViewPropTypes.style,
contentProps: PropTypes.object,
scrollWithoutAnimation: PropTypes.bool,
locked: PropTypes.bool,
prerenderingSiblingsNumber: PropTypes.number,
collapsableBar: PropTypes.node
}
const styles = StyleSheet.create({
container: {
flex: 1
},
scrollableContentAndroid: {
flex: 1
}
})
module.exports = {
DefaultTabBar,
ScrollableTabBar,
ScrollableTabView
}
import React from 'react'
import ReactNative from 'react-native'
const {View, StyleSheet} = ReactNative
import StaticContainer from './StaticContainer'
export default SceneComponent = (Props) => {
const {shouldUpdated, ...props} = Props
return <View {...props}>
<StaticContainer shouldUpdate={shouldUpdated}>
{props.children}
</StaticContainer>
</View>
}
import React, {Component} from 'react'
import ReactNative from 'react-native'
import PropTypes from 'prop-types'
const {
ViewPropTypes,
Dimensions,
View,
Animated,
ScrollView,
StyleSheet,
InteractionManager,
Platform,
Text,
I18nManager
} = ReactNative
import Button from './Button'
const WINDOW_WIDTH = Dimensions.get('window').width
export default class ScrollableTabBar extends Component {
constructor(props) {
super(props)
this.state = this.getInitialState()
this.updateView = this.updateView.bind(this)
this.necessarilyMeasurementsCompleted = this.necessarilyMeasurementsCompleted.bind(this)
this.updateTabPanel = this.updateTabPanel.bind(this)
this.updateTabUnderline = this.updateTabUnderline.bind(this)
this.updateTabUnderline = this.updateTabUnderline.bind(this)
this.renderTab = this.renderTab.bind(this)
this.measureTab = this.measureTab.bind(this)
this.onTabContainerLayout = this.onTabContainerLayout.bind(this)
this.onContainerLayout = this.onContainerLayout.bind(this)
}
getInitialState() {
this._tabsMeasurements = []
return {
_leftTabUnderline: new Animated.Value(0),
_widthTabUnderline: new Animated.Value(0),
_containerWidth: null
}
}
componentDidMount() {
this.props.scrollValue.addListener(this.updateView)
}
updateView(offset) {
const position = Math.floor(offset.value)
const pageOffset = offset.value % 1
const tabCount = this.props.tabs.length
const lastTabPosition = tabCount - 1
if (tabCount === 0 || offset.value < 0 || offset.value > lastTabPosition) {
return
}
if (this.necessarilyMeasurementsCompleted(position, position === lastTabPosition)) {
this.updateTabPanel(position, pageOffset)
this.updateTabUnderline(position, pageOffset, tabCount)
}
}
necessarilyMeasurementsCompleted(position, isLastTab) {
return this._tabsMeasurements[position] &&
(isLastTab || this._tabsMeasurements[position + 1]) &&
this._tabContainerMeasurements &&
this._containerMeasurements
}
updateTabPanel(position, pageOffset) {
const containerWidth = this._containerMeasurements.width
const tabWidth = this._tabsMeasurements[position].width
const nextTabMeasurements = this._tabsMeasurements[position + 1]
const nextTabWidth = nextTabMeasurements && nextTabMeasurements.width || 0
const tabOffset = this._tabsMeasurements[position].left
const absolutePageOffset = pageOffset * tabWidth
let newScrollX = tabOffset + absolutePageOffset
// center tab and smooth tab change (for when tabWidth changes a lot between two tabs)
newScrollX -= (containerWidth - (1 - pageOffset) * tabWidth - pageOffset * nextTabWidth) / 2
newScrollX = newScrollX >= 0 ? newScrollX : 0
if (Platform.OS === 'android') {
this._scrollView && this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false})
} else {
const rightBoundScroll = this._tabContainerMeasurements.width - (this._containerMeasurements.width)
newScrollX = newScrollX > rightBoundScroll ? rightBoundScroll : newScrollX
this._scrollView && this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false})
}
}
updateTabUnderline(position, pageOffset, tabCount) {
const lineLeft = this._tabsMeasurements[position].left
const lineRight = this._tabsMeasurements[position].right
if (position < tabCount - 1) {
const nextTabLeft = this._tabsMeasurements[position + 1].left
const nextTabRight = this._tabsMeasurements[position + 1].right
const newLineLeft = (pageOffset * nextTabLeft + (1 - pageOffset) * lineLeft)
const newLineRight = (pageOffset * nextTabRight + (1 - pageOffset) * lineRight)
this.state._leftTabUnderline.setValue(newLineLeft)
this.state._widthTabUnderline.setValue(newLineRight - newLineLeft)
} else {
this.state._leftTabUnderline.setValue(lineLeft)
this.state._widthTabUnderline.setValue(lineRight - lineLeft)
}
}
renderTab(name, page, isTabActive, onPressHandler, onLayoutHandler) {
const {activeTextColor, inactiveTextColor, textStyle} = this.props
const textColor = isTabActive ? activeTextColor : inactiveTextColor
const fontWeight = isTabActive ? 'bold' : 'normal'
return <Button
key={`${name}_${page}`}
accessible={true}
accessibilityLabel={name}
accessibilityTraits='button'
onPress={() => onPressHandler(page)}
onLayout={onLayoutHandler}
>
<View style={[styles.tab, this.props.tabStyle]}>
<Text style={[{color: textColor, fontWeight}, textStyle]}>
{name}
</Text>
</View>
</Button>
}
measureTab(page, event) {
const {x, width, height} = event.nativeEvent.layout
this._tabsMeasurements[page] = {left: x, right: x + width, width, height}
this.updateView({value: this.props.scrollValue._value})
}
render() {
const tabUnderlineStyle = {
position: 'absolute',
height: 4,
backgroundColor: 'navy',
bottom: 0
}
const key = I18nManager.isRTL ? 'right' : 'left'
const dynamicTabUnderline = {
[`${key}`]: this.state._leftTabUnderline,
width: this.state._widthTabUnderline
}
return <View
style={[styles.container, {backgroundColor: this.props.backgroundColor}, this.props.style]}
onLayout={this.onContainerLayout}
>
<ScrollView
automaticallyAdjustContentInsets={false}
ref={(scrollView) => {
this._scrollView = scrollView
}}
horizontal={true}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
directionalLockEnabled={true}
onScroll={this.props.onScroll}
bounces={false}
scrollsToTop={false}
>
<View
style={[styles.tabs, {width: this.state._containerWidth}, this.props.tabsContainerStyle]}
ref={'tabContainer'}
onLayout={this.onTabContainerLayout}
>
{this.props.tabs.map((name, page) => {
const isTabActive = this.props.activeTab === page
const renderTab = this.props.renderTab || this.renderTab
return renderTab(name, page, isTabActive, this.props.goToPage, this.measureTab.bind(this, page))
})}
<Animated.View style={[tabUnderlineStyle, dynamicTabUnderline, this.props.underlineStyle]}/>
</View>
</ScrollView>
</View>
}
componentWillReceiveProps(nextProps) {
// If the tabs change, force the width of the tabs container to be recalculated
if (JSON.stringify(this.props.tabs) !== JSON.stringify(nextProps.tabs) && this.state._containerWidth) {
this.setState({_containerWidth: null})
}
}
onTabContainerLayout(e) {
this._tabContainerMeasurements = e.nativeEvent.layout
let width = this._tabContainerMeasurements.width
if (width < WINDOW_WIDTH) {
width = WINDOW_WIDTH
}
this.setState({_containerWidth: width})
this.updateView({value: this.props.scrollValue._value})
}
onContainerLayout(e) {
this._containerMeasurements = e.nativeEvent.layout
this.updateView({value: this.props.scrollValue._value})
}
}
ScrollableTabBar.defaultProps = {
scrollOffset: 52,
activeTextColor: 'navy',
inactiveTextColor: 'black',
backgroundColor: null,
style: {},
tabStyle: {},
tabsContainerStyle: {},
underlineStyle: {}
}
ScrollableTabBar.propTypes = {
goToPage: PropTypes.func,
activeTab: PropTypes.number,
tabs: PropTypes.array,
backgroundColor: PropTypes.string,
activeTextColor: PropTypes.string,
inactiveTextColor: PropTypes.string,
scrollOffset: PropTypes.number,
style: ViewPropTypes.style,
tabStyle: ViewPropTypes.style,
tabsContainerStyle: ViewPropTypes.style,
textStyle: Text.propTypes.style,
renderTab: PropTypes.func,
underlineStyle: ViewPropTypes.style,
onScroll: PropTypes.func
}
const styles = StyleSheet.create({
tab: {
height: 49,
alignItems: 'center',
justifyContent: 'center',
paddingLeft: 20,
paddingRight: 20
},
container: {
height: 50,
borderWidth: 1,
borderTopWidth: 0,
borderLeftWidth: 0,
borderRightWidth: 0,
borderColor: '#ccc'
},
tabs: {
flexDirection: 'row',
justifyContent: 'space-around'
}
})
import React, {Component} from 'react'
export default class StaticContainer extends Component {
shouldComponentUpdate(nextProps: Object): boolean {
return !!nextProps.shouldUpdate
}
render(): ?ReactElement {
const child = this.props.children
if (child === null || child === false) {
return null
}
return React.Children.only(child)
}
}
+4
-6
{
"name": "@valdio/react-native-scrollable-tabview",
"version": "0.8.9",
"version": "0.8.10",
"description": "",
"main": "index.js",
"main": "lib/index.js",
"scripts": {

@@ -22,3 +22,3 @@ "lint": "eslint -c .eslintrc . --ignore-path .gitignore",

],
"author": "Valdio Veliu",
"author": "Valdio Veliu <Valdio Veliu>",
"license": "MIT",

@@ -30,6 +30,4 @@ "bugs": {

"homepage": "https://github.com/valdio/react-native-scrollable-tabview#readme",
"dependencies": {
"react-timer-mixin": "^0.13.3"
},
"dependencies": {},
"devDependencies": {}
}

@@ -113,3 +113,4 @@

- **`tabBarTextStyle`** _(Object)_ - Additional styles to the tab bar's text. Example: `{fontFamily: 'Roboto', fontSize: 15}`
- **`style`** _([View.propTypes.style](https://facebook.github.io/react-native/docs/view.html#style))_
- **`style`** _([View.propTypes.style](https://facebook.github.io/react-native/docs/view.html#style))_ - Container style
- **`contentStyle`** _([View.propTypes.style](https://facebook.github.io/react-native/docs/view.html#style))_ - Content style
- **`contentProps`** _(Object)_ - props that are applied to root `ScrollView`/`ViewPagerAndroid`. Note that overriding defaults set by the library may break functionality; see the source for details.

@@ -133,3 +134,3 @@ - **`scrollWithoutAnimation`** _(Bool)_ - on tab press change tab without animation.

_onRefresh = (callback) => {
networkReqyest().then(response => callback(response))
networkRequest().then(response => callback(response))
}

@@ -136,0 +137,0 @@

import React from 'react'
import ReactNative from 'react-native'
const {
TouchableNativeFeedback,
View
} = ReactNative
export default Button = (props) =>
<TouchableNativeFeedback
delayPressIn={0}
background={TouchableNativeFeedback.SelectableBackground()} // eslint-disable-line new-cap
{...props}
>
{props.children}
</TouchableNativeFeedback>
import React from 'react'
import ReactNative from 'react-native'
const {
TouchableOpacity,
View
} = ReactNative
export default Button = (props) => {
return <TouchableOpacity {...props}>
{props.children}
</TouchableOpacity>
}
import React from 'react'
import ReactNative from 'react-native'
const {
TouchableOpacity,
View
} = ReactNative
export default Button = (props) => {
return <TouchableOpacity {...props}>
{props.children}
</TouchableOpacity>
}
import React, {Component} from 'react'
import ReactNative from 'react-native'
import PropTypes from 'prop-types'
const {
StyleSheet,
Text,
View,
ViewPropTypes,
Animated
} = ReactNative
import Button from './Button'
import ScrollableTabView from './index'
const defaultProps = {
activeTextColor: 'navy',
inactiveTextColor: 'black',
backgroundColor: null
}
export default class DefaultTabBar extends React.Component {
constructor(props) {
super(props)
this.renderTab = this.renderTab.bind(this)
}
renderTabOption(name, page) {
}
renderTab(name, page, isTabActive, onPressHandler) {
const {activeTextColor, inactiveTextColor, textStyle} = this.props
const textColor = isTabActive ? activeTextColor : inactiveTextColor
const fontWeight = isTabActive ? 'bold' : 'normal'
return <Button
style={styles.flexOne}
key={name}
accessible={true}
accessibilityLabel={name}
accessibilityTraits='button'
onPress={() => onPressHandler(page)}
>
<View style={[styles.tab, this.props.tabStyle]}>
<Text style={[{color: textColor, fontWeight}, textStyle]}>
{name}
</Text>
</View>
</Button>
}
render() {
const containerWidth = this.props.containerWidth
const numberOfTabs = this.props.tabs.length
const tabUnderlineStyle = {
position: 'absolute',
width: containerWidth / numberOfTabs,
height: 4,
backgroundColor: 'navy',
bottom: 0
}
const left = this.props.scrollValue.interpolate({
inputRange: [0, 1], outputRange: [0, containerWidth / numberOfTabs]
})
return (
<View style={[styles.tabs, {backgroundColor: this.props.backgroundColor}, this.props.style]}>
{this.props.tabs.map((name, page) => {
const isTabActive = this.props.activeTab === page
const renderTab = this.props.renderTab || this.renderTab
return renderTab(name, page, isTabActive, this.props.goToPage) // () =>
})}
<Animated.View style={[tabUnderlineStyle, {left}, this.props.underlineStyle]}/>
</View>
)
}
}
DefaultTabBar.propTypes = {
goToPage: PropTypes.func,
activeTab: PropTypes.number,
tabs: PropTypes.array,
backgroundColor: PropTypes.string,
activeTextColor: PropTypes.string,
inactiveTextColor: PropTypes.string,
textStyle: Text.propTypes.style,
tabStyle: ViewPropTypes.style,
renderTab: PropTypes.func,
underlineStyle: ViewPropTypes.style
}
DefaultTabBar.defaultProps = {
activeTextColor: 'navy',
inactiveTextColor: 'black',
backgroundColor: null
}
const styles = StyleSheet.create({
tab: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingBottom: 10
},
flexOne: {
flex: 1
},
tabs: {
height: 50,
flexDirection: 'row',
justifyContent: 'space-around',
borderWidth: 1,
borderTopWidth: 0,
borderLeftWidth: 0,
borderRightWidth: 0,
borderColor: '#ccc'
}
})
import React, {Component} from 'react'
import ReactNative from 'react-native'
import PropTypes from 'prop-types'
const {
ViewPropTypes,
Dimensions,
View,
Animated,
ScrollView,
StyleSheet,
InteractionManager,
Platform,
RefreshControl
} = ReactNative
import TimerMixin from 'react-timer-mixin'
import SceneComponent from './SceneComponent'
import DefaultTabBar from './DefaultTabBar'
import ScrollableTabBar from './ScrollableTabBar'
/**
* pullToRefresh: function used to trigger pull to refresh action.
* Function muns have a callback response in order to stop pull to refresh action.
* refreshControlStyle: style of RefreshControl
*/
class ScrollableTabView extends Component {
constructor(props) {
super(props)
this.state = this.getInitialState()
this._handleLayout = this._handleLayout.bind(this)
this.goToPage = this.goToPage.bind(this)
this.renderTabBar = this.renderTabBar.bind(this)
this.updateSceneKeys = this.updateSceneKeys.bind(this)
this.newSceneKeys = this.newSceneKeys.bind(this)
this._shouldRenderSceneKey = this._shouldRenderSceneKey.bind(this)
this._keyExists = this._keyExists.bind(this)
this._makeSceneKey = this._makeSceneKey.bind(this)
this.renderScrollableContent = this.renderScrollableContent.bind(this)
this._composeScenes = this._composeScenes.bind(this)
this._onMomentumScrollBeginAndEnd = this._onMomentumScrollBeginAndEnd.bind(this)
this._updateSelectedPage = this._updateSelectedPage.bind(this)
this.renderCollapsableBar = this.renderCollapsableBar.bind(this)
}
mixins = [TimerMixin]
/* getDefaultProps() {
return {
tabBarPosition: 'top',
initialPage: 0,
page: -1,
onChangeTab: () => {
},
onScroll: () => {
},
contentProps: {},
scrollWithoutAnimation: false,
locked: false,
prerenderingSiblingsNumber: 0
}
}*/
getInitialState() {
const width = Dimensions.get('window').width
return {
currentPage: this.props.initialPage,
scrollX: new Animated.Value(this.props.initialPage * width),
scrollValue: new Animated.Value(this.props.initialPage),
containerWidth: width,
sceneKeys: this.newSceneKeys({currentPage: this.props.initialPage}),
refreshing: false
}
}
_onRefresh = () => {
//if there is not pullToRefresh function do nothing
if (!this.props.pullToRefresh)
return
this.setState({refreshing: true})
this.props.pullToRefresh(response => {
this.setState({refreshing: false})
})
}
componentDidMount() {
setTimeout(() => {
InteractionManager.runAfterInteractions(() => {
if (Platform.OS === 'android') {
this.goToPage(this.props.initialPage, false)
}
})
}, 0)
this.state.scrollX.addListener(({value}) => {
const scrollValue = value / this.state.containerWidth
this.state.scrollValue.setValue(scrollValue)
this.props.onScroll(scrollValue)
})
}
componentWillReceiveProps(props) {
if (props.children !== this.props.children) {
this.updateSceneKeys({page: this.state.currentPage, children: props.children})
}
if (props.page >= 0 && props.page !== this.state.currentPage) {
this.goToPage(props.page)
}
}
goToPage(pageNumber, animated = !this.props.scrollWithoutAnimation) {
const offset = pageNumber * this.state.containerWidth
if (this.scrollView && this.scrollView._component && this.scrollView._component.scrollTo) {
this.scrollView._component.scrollTo({x: offset, y: 0, animated})
}
const currentPage = this.state.currentPage
this.updateSceneKeys({
page: pageNumber,
callback: this._onChangeTab.bind(this, currentPage, pageNumber)
})
}
renderTabBar(props) {
if (this.props.renderTabBar === false) {
return null
} else if (this.props.renderTabBar) {
return React.cloneElement(this.props.renderTabBar(props), props)
} else {
return <DefaultTabBar {...props} />
}
}
updateSceneKeys({
page, children = this.props.children, callback = () => {
}
}) {
let newKeys = this.newSceneKeys({previousKeys: this.state.sceneKeys, currentPage: page, children})
this.setState({currentPage: page, sceneKeys: newKeys}, callback)
}
newSceneKeys({previousKeys = [], currentPage = 0, children = this.props.children}) {
let newKeys = []
this._children(children).forEach((child, idx) => {
let key = this._makeSceneKey(child, idx)
if (this._keyExists(previousKeys, key) ||
this._shouldRenderSceneKey(idx, currentPage)) {
newKeys.push(key)
}
})
return newKeys
}
_shouldRenderSceneKey(idx, currentPageKey) {
let numOfSibling = this.props.prerenderingSiblingsNumber
return (idx < (currentPageKey + numOfSibling + 1) &&
idx > (currentPageKey - numOfSibling - 1))
}
_keyExists(sceneKeys, key) {
return sceneKeys.find((sceneKey) => key === sceneKey)
}
_makeSceneKey(child, idx) {
return child.props.tabLabel + '_' + idx
}
renderScrollableContent() {
//in case of the collapsible scroll view the pull to refresh animation will be applied on the container
//on the other case the refresh animations will be applied here.
const isContainerScrollView = !!this.props.collapsableBar
const scenes = this._composeScenes()
return <Animated.ScrollView
refreshControl={!isContainerScrollView && this.props.pullToRefresh && typeof this.props.pullToRefresh === 'function' &&
<RefreshControl style={this.props.refreshControlStyle || {}}
refreshing={this.state.refreshing}
onRefresh={this._onRefresh}/> || undefined}
showsVerticalScrollIndicator={this.props.showsVerticalScrollIndicator}
showsHorizontalScrollIndicator={this.props.showsHorizontalScrollIndicator}
horizontal
pagingEnabled
automaticallyAdjustContentInsets={false}
contentOffset={{x: this.props.initialPage * this.state.containerWidth}}
ref={scrollView => this.scrollView = scrollView}
onScroll={Animated.event([{nativeEvent: {contentOffset: {x: this.state.scrollX}}}], {useNativeDriver: true})}
onMomentumScrollBegin={this._onMomentumScrollBeginAndEnd}
onMomentumScrollEnd={this._onMomentumScrollBeginAndEnd}
scrollEventThrottle={16}
scrollsToTop={false}
scrollEnabled={!this.props.locked}
directionalLockEnabled
alwaysBounceVertical={false}
keyboardDismissMode="on-drag"
{...this.props.contentProps}
>
{scenes}
</Animated.ScrollView>
}
_composeScenes() {
return this._children().map((child, idx) => {
let key = this._makeSceneKey(child, idx)
let element
if (!!this.props.collapsableBar) {
element = this.state.currentPage === idx ? child : null
} else {
element = this._keyExists(this.state.sceneKeys, key) ? child : <View tabLabel={child.props.tabLabel}/>
}
return <SceneComponent
key={child.key}
shouldUpdated={!!this.props.collapsableBar || this._shouldRenderSceneKey(idx, this.state.currentPage)}
style={{width: this.state.containerWidth}}
>
{element}
</SceneComponent>
})
}
_onMomentumScrollBeginAndEnd(e) {
const offsetX = e.nativeEvent.contentOffset.x
const page = Math.round(offsetX / this.state.containerWidth)
if (this.state.currentPage !== page) {
this._updateSelectedPage(page)
}
}
_updateSelectedPage(nextPage) {
let localNextPage = nextPage
if (typeof localNextPage === 'object') {
localNextPage = nextPage.nativeEvent.position
}
const currentPage = this.state.currentPage
this.updateSceneKeys({
page: localNextPage,
callback: this._onChangeTab.bind(this, currentPage, localNextPage)
})
}
_onChangeTab(prevPage, currentPage) {
this.props.onChangeTab({
i: currentPage,
ref: this._children()[currentPage],
from: prevPage
})
if (this.contentScrollDistance >= this.collapsableBarHeight) {
this.contentView.scrollTo({x: 0, y: this.collapsableBarHeight, animated: false})
}
}
_handleLayout(e) {
const {width} = e.nativeEvent.layout
if (Math.round(width) !== Math.round(this.state.containerWidth)) {
this.setState({containerWidth: width})
this.requestAnimationFrame(() => {
this.goToPage(this.state.currentPage)
})
}
}
_children(children = this.props.children) {
return React.Children.map(children, (child) => child)
}
renderCollapsableBar() {
if (!this.props.collapsableBar) {
return null
}
return React.cloneElement(this.props.collapsableBar, {
onLayout: event => {
this.collapsableBarHeight = event.nativeEvent.layout.height
}
})
}
render() {
let overlayTabs = (this.props.tabBarPosition === 'overlayTop' || this.props.tabBarPosition === 'overlayBottom')
let tabBarProps = {
goToPage: this.goToPage,
tabs: this._children().map((child) => child.props.tabLabel),
activeTab: this.state.currentPage,
scrollX: this.state.scrollX,
scrollValue: this.state.scrollValue,
containerWidth: this.state.containerWidth
}
if (this.props.tabBarBackgroundColor) {
tabBarProps.backgroundColor = this.props.tabBarBackgroundColor
}
if (this.props.tabBarActiveTextColor) {
tabBarProps.activeTextColor = this.props.tabBarActiveTextColor
}
if (this.props.tabBarInactiveTextColor) {
tabBarProps.inactiveTextColor = this.props.tabBarInactiveTextColor
}
if (this.props.tabBarTextStyle) {
tabBarProps.textStyle = this.props.tabBarTextStyle
}
if (this.props.tabBarUnderlineStyle) {
tabBarProps.underlineStyle = this.props.tabBarUnderlineStyle
}
if (overlayTabs) {
tabBarProps.style = {
position: 'absolute',
left: 0,
right: 0,
[this.props.tabBarPosition === 'overlayTop' ? 'top' : 'bottom']: 0
}
}
const ContainerView = this.props.collapsableBar ? ScrollView : View
const isScrollView = this.props.collapsableBar ? true : false
return (<ContainerView
refreshControl={isScrollView && this.props.pullToRefresh && typeof this.props.pullToRefresh === 'function' &&
<RefreshControl style={this.props.refreshControlStyle || {}}
refreshing={this.state.refreshing}
onRefresh={this._onRefresh}/> || undefined}
showsVerticalScrollIndicator={isScrollView && this.props.showsVerticalScrollIndicator}
showsHorizontalScrollIndicator={isScrollView && this.props.showsHorizontalScrollIndicator}
style={[styles.container, this.props.style]}
onLayout={this._handleLayout} //()=>
ref={contentView => this.contentView = contentView}
onMomentumScrollEnd={event => {
this.contentScrollDistance = event.nativeEvent.contentOffset.y
}}
stickyHeaderIndices={this.props.collapsableBar ? [1] : []}>
{this.renderCollapsableBar()}
{this.props.tabBarPosition === 'top' && this.renderTabBar(tabBarProps)}
{this.renderScrollableContent()}
{(this.props.tabBarPosition === 'bottom' || overlayTabs) && this.renderTabBar(tabBarProps)}
</ContainerView>
)
}
}
ScrollableTabView.defaultProps = {
tabBarPosition: 'top',
initialPage: 0,
page: -1,
onChangeTab: () => {
},
onScroll: () => {
},
contentProps: {},
scrollWithoutAnimation: false,
locked: false,
prerenderingSiblingsNumber: 0
}
ScrollableTabView.propTypes = {
tabBarPosition: PropTypes.oneOf(['top', 'bottom', 'overlayTop', 'overlayBottom']),
initialPage: PropTypes.number,
page: PropTypes.number,
onChangeTab: PropTypes.func,
onScroll: PropTypes.func,
renderTabBar: PropTypes.any,
style: ViewPropTypes.style,
contentProps: PropTypes.object,
scrollWithoutAnimation: PropTypes.bool,
locked: PropTypes.bool,
prerenderingSiblingsNumber: PropTypes.number,
collapsableBar: PropTypes.node
}
const styles = StyleSheet.create({
container: {
flex: 1
},
scrollableContentAndroid: {
flex: 1
}
})
module.exports = {
DefaultTabBar,
ScrollableTabBar,
ScrollableTabView
}
import React from 'react'
import ReactNative from 'react-native'
const {View, StyleSheet} = ReactNative
import StaticContainer from './StaticContainer'
export default SceneComponent = (Props) => {
const {shouldUpdated, ...props} = Props
return <View {...props}>
<StaticContainer shouldUpdate={shouldUpdated}>
{props.children}
</StaticContainer>
</View>
}
import React, {Component} from 'react'
import ReactNative from 'react-native'
import PropTypes from 'prop-types'
const {
ViewPropTypes,
Dimensions,
View,
Animated,
ScrollView,
StyleSheet,
InteractionManager,
Platform,
Text,
I18nManager
} = ReactNative
import Button from './Button'
const WINDOW_WIDTH = Dimensions.get('window').width
export default class ScrollableTabBar extends Component {
constructor(props) {
super(props)
this.state = this.getInitialState()
this.updateView = this.updateView.bind(this)
this.necessarilyMeasurementsCompleted = this.necessarilyMeasurementsCompleted.bind(this)
this.updateTabPanel = this.updateTabPanel.bind(this)
this.updateTabUnderline = this.updateTabUnderline.bind(this)
this.updateTabUnderline = this.updateTabUnderline.bind(this)
this.renderTab = this.renderTab.bind(this)
this.measureTab = this.measureTab.bind(this)
this.onTabContainerLayout = this.onTabContainerLayout.bind(this)
this.onContainerLayout = this.onContainerLayout.bind(this)
}
getInitialState() {
this._tabsMeasurements = []
return {
_leftTabUnderline: new Animated.Value(0),
_widthTabUnderline: new Animated.Value(0),
_containerWidth: null
}
}
componentDidMount() {
this.props.scrollValue.addListener(this.updateView)
}
updateView(offset) {
const position = Math.floor(offset.value)
const pageOffset = offset.value % 1
const tabCount = this.props.tabs.length
const lastTabPosition = tabCount - 1
if (tabCount === 0 || offset.value < 0 || offset.value > lastTabPosition) {
return
}
if (this.necessarilyMeasurementsCompleted(position, position === lastTabPosition)) {
this.updateTabPanel(position, pageOffset)
this.updateTabUnderline(position, pageOffset, tabCount)
}
}
necessarilyMeasurementsCompleted(position, isLastTab) {
return this._tabsMeasurements[position] &&
(isLastTab || this._tabsMeasurements[position + 1]) &&
this._tabContainerMeasurements &&
this._containerMeasurements
}
updateTabPanel(position, pageOffset) {
const containerWidth = this._containerMeasurements.width
const tabWidth = this._tabsMeasurements[position].width
const nextTabMeasurements = this._tabsMeasurements[position + 1]
const nextTabWidth = nextTabMeasurements && nextTabMeasurements.width || 0
const tabOffset = this._tabsMeasurements[position].left
const absolutePageOffset = pageOffset * tabWidth
let newScrollX = tabOffset + absolutePageOffset
// center tab and smooth tab change (for when tabWidth changes a lot between two tabs)
newScrollX -= (containerWidth - (1 - pageOffset) * tabWidth - pageOffset * nextTabWidth) / 2
newScrollX = newScrollX >= 0 ? newScrollX : 0
if (Platform.OS === 'android') {
this._scrollView && this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false})
} else {
const rightBoundScroll = this._tabContainerMeasurements.width - (this._containerMeasurements.width)
newScrollX = newScrollX > rightBoundScroll ? rightBoundScroll : newScrollX
this._scrollView && this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false})
}
}
updateTabUnderline(position, pageOffset, tabCount) {
const lineLeft = this._tabsMeasurements[position].left
const lineRight = this._tabsMeasurements[position].right
if (position < tabCount - 1) {
const nextTabLeft = this._tabsMeasurements[position + 1].left
const nextTabRight = this._tabsMeasurements[position + 1].right
const newLineLeft = (pageOffset * nextTabLeft + (1 - pageOffset) * lineLeft)
const newLineRight = (pageOffset * nextTabRight + (1 - pageOffset) * lineRight)
this.state._leftTabUnderline.setValue(newLineLeft)
this.state._widthTabUnderline.setValue(newLineRight - newLineLeft)
} else {
this.state._leftTabUnderline.setValue(lineLeft)
this.state._widthTabUnderline.setValue(lineRight - lineLeft)
}
}
renderTab(name, page, isTabActive, onPressHandler, onLayoutHandler) {
const {activeTextColor, inactiveTextColor, textStyle} = this.props
const textColor = isTabActive ? activeTextColor : inactiveTextColor
const fontWeight = isTabActive ? 'bold' : 'normal'
return <Button
key={`${name}_${page}`}
accessible={true}
accessibilityLabel={name}
accessibilityTraits='button'
onPress={() => onPressHandler(page)}
onLayout={onLayoutHandler}
>
<View style={[styles.tab, this.props.tabStyle]}>
<Text style={[{color: textColor, fontWeight}, textStyle]}>
{name}
</Text>
</View>
</Button>
}
measureTab(page, event) {
const {x, width, height} = event.nativeEvent.layout
this._tabsMeasurements[page] = {left: x, right: x + width, width, height}
this.updateView({value: this.props.scrollValue._value})
}
render() {
const tabUnderlineStyle = {
position: 'absolute',
height: 4,
backgroundColor: 'navy',
bottom: 0
}
const key = I18nManager.isRTL ? 'right' : 'left'
const dynamicTabUnderline = {
[`${key}`]: this.state._leftTabUnderline,
width: this.state._widthTabUnderline
}
return <View
style={[styles.container, {backgroundColor: this.props.backgroundColor}, this.props.style]}
onLayout={this.onContainerLayout}
>
<ScrollView
automaticallyAdjustContentInsets={false}
ref={(scrollView) => {
this._scrollView = scrollView
}}
horizontal={true}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
directionalLockEnabled={true}
onScroll={this.props.onScroll}
bounces={false}
scrollsToTop={false}
>
<View
style={[styles.tabs, {width: this.state._containerWidth}, this.props.tabsContainerStyle]}
ref={'tabContainer'}
onLayout={this.onTabContainerLayout}
>
{this.props.tabs.map((name, page) => {
const isTabActive = this.props.activeTab === page
const renderTab = this.props.renderTab || this.renderTab
return renderTab(name, page, isTabActive, this.props.goToPage, this.measureTab.bind(this, page))
})}
<Animated.View style={[tabUnderlineStyle, dynamicTabUnderline, this.props.underlineStyle]}/>
</View>
</ScrollView>
</View>
}
componentWillReceiveProps(nextProps) {
// If the tabs change, force the width of the tabs container to be recalculated
if (JSON.stringify(this.props.tabs) !== JSON.stringify(nextProps.tabs) && this.state._containerWidth) {
this.setState({_containerWidth: null})
}
}
onTabContainerLayout(e) {
this._tabContainerMeasurements = e.nativeEvent.layout
let width = this._tabContainerMeasurements.width
if (width < WINDOW_WIDTH) {
width = WINDOW_WIDTH
}
this.setState({_containerWidth: width})
this.updateView({value: this.props.scrollValue._value})
}
onContainerLayout(e) {
this._containerMeasurements = e.nativeEvent.layout
this.updateView({value: this.props.scrollValue._value})
}
}
ScrollableTabBar.defaultProps = {
scrollOffset: 52,
activeTextColor: 'navy',
inactiveTextColor: 'black',
backgroundColor: null,
style: {},
tabStyle: {},
tabsContainerStyle: {},
underlineStyle: {}
}
ScrollableTabBar.propTypes = {
goToPage: PropTypes.func,
activeTab: PropTypes.number,
tabs: PropTypes.array,
backgroundColor: PropTypes.string,
activeTextColor: PropTypes.string,
inactiveTextColor: PropTypes.string,
scrollOffset: PropTypes.number,
style: ViewPropTypes.style,
tabStyle: ViewPropTypes.style,
tabsContainerStyle: ViewPropTypes.style,
textStyle: Text.propTypes.style,
renderTab: PropTypes.func,
underlineStyle: ViewPropTypes.style,
onScroll: PropTypes.func
}
const styles = StyleSheet.create({
tab: {
height: 49,
alignItems: 'center',
justifyContent: 'center',
paddingLeft: 20,
paddingRight: 20
},
container: {
height: 50,
borderWidth: 1,
borderTopWidth: 0,
borderLeftWidth: 0,
borderRightWidth: 0,
borderColor: '#ccc'
},
tabs: {
flexDirection: 'row',
justifyContent: 'space-around'
}
})
import React, {Component} from 'react'
export default class StaticContainer extends Component {
shouldComponentUpdate(nextProps: Object): boolean {
return !!nextProps.shouldUpdate
}
render(): ?ReactElement {
const child = this.props.children
if (child === null || child === false) {
return null
}
return React.Children.only(child)
}
}