Merge pull request #72 from kirankalyan5/setup-flow-type

#61 Set up and use flow-type 💫
This commit is contained in:
Kiran Kalyan
2018-10-19 11:33:35 +02:00
committed by GitHub
9 changed files with 9280 additions and 232 deletions

6
.babelrc Normal file
View File

@@ -0,0 +1,6 @@
{
"presets": [
"react-native",
"flow"
]
}

277
.eslintrc
View File

@@ -1,89 +1,200 @@
{
"root": true,
"parserOptions": {
"sourceType": "module",
"ecmaFeatures": {
"jsx": true,
"modules": true,
"experimentalObjectRestSpread": true
}
},
"env": {
"browser": true,
"node": true,
"es6": true
},
"globals": {
"jest": false,
"describe": false,
"it": false,
"afterEach": false,
"beforeEach": false,
"expect": false,
"spyOn": false
},
"parser": "babel-eslint",
"extends": "airbnb",
"plugins": [
"react",
"react-native"
"flowtype",
"jsx-a11y",
"import"
],
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
// details: https://github.com/yannickcr/eslint-plugin-react
"rules": {
"semi": ['error', "never"],
"quotes": ['error', "single"],
"no-unused-vars": 'error',
"no-irregular-whitespace": ['error', { "skipComments": true }],
"no-console": ['error', { "allow": ["log", "warn", 'error'] }],
"no-mixed-spaces-and-tabs": ['error', "smart-tabs"],
"react/display-name": ['error', { "ignoreTranspilerName": false }],
"react/forbid-prop-types": 'error',
"react/jsx-boolean-value": 'error',
"react/jsx-closing-bracket-location": 'off',
"react/jsx-curly-spacing": 'off',
"react/jsx-handler-names": 'error',
"react/jsx-indent-props": ['error',4],
"react/jsx-indent": ['error',4],
"react/jsx-key": 'error',
"react/jsx-max-props-per-line": 'error',
"react/jsx-no-bind": ['error', {
"ignoreRefs": false,
"allowArrowFunctions": true,
"allowBind": false
}],
"react/jsx-no-duplicate-props": 'error',
"react/jsx-no-literals": 'off',
"react/jsx-no-undef": 'error',
"react/jsx-pascal-case": 'error',
"jsx-quotes": ['error', "prefer-double"],
"react/sort-prop-types": 'error',
"react/jsx-sort-props": 'off',
"react/jsx-uses-react": 'error',
"react/jsx-uses-vars": 'error',
"react/no-danger": 'error',
"react/no-deprecated": 'error',
"react/no-did-mount-set-state": 'error',
"react/no-did-update-set-state": 'error',
"react/no-direct-mutation-state": 'error',
"react/no-is-mounted": 'error',
"react/no-multi-comp": 'error',
"react/no-set-state": 'off',
"react/no-string-refs": 'error',
"react/no-unknown-property": 'error',
"react/prefer-es6-class": 'error',
"react/prop-types": 'error',
"react/react-in-jsx-scope": 'error',
"react/require-extension": 'error',
"react/self-closing-comp": 'error',
"react/sort-comp": 'error',
"react/wrap-multilines": 'error',
// details: https://github.com/Intellicode/eslint-plugin-react-native
"react-native/no-unused-styles": 2,
"react-native/split-platform-components": 2,
"react-native/no-inline-styles": 2,
"react-native/no-color-literals": 2
// Best Practices
"accessor-pairs": 2,
"block-scoped-var": 2,
"complexity": [
"error",
10
],
"consistent-return": 2,
"curly": 2,
"default-case": 2,
"dot-location": [
2,
"property"
],
"dot-notation": 2,
"eqeqeq": [
2,
"allow-null"
],
"guard-for-in": 2,
"no-alert": 2,
"no-caller": 2,
"no-case-declarations": 2,
"no-div-regex": 2,
"no-else-return": 2,
"no-empty-pattern": 2,
"no-eq-null": 2,
"no-eval": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-implicit-coercion": 2,
"no-implied-eval": 2,
"no-invalid-this": 2,
"no-iterator": 2,
"no-labels": 2,
"no-lone-blocks": 2,
"no-loop-func": 2,
"no-magic-numbers": 0,
"no-multi-str": 2,
"no-native-reassign": 2,
"no-new-func": 2,
"no-new-wrappers": 2,
"no-new": 2,
"no-octal-escape": 2,
"no-octal": 2,
"no-param-reassign": 2,
"no-process-env": 0,
"no-proto": 2,
"no-redeclare": 2,
"no-return-assign": 2,
"no-script-url": 2,
"no-self-compare": 2,
"no-sequences": 2,
"no-throw-literal": 2,
"no-unsafe-negation": 2,
"no-unused-expressions": 2,
"no-useless-call": 2,
"no-useless-concat": 2,
"no-useless-rename": 0,
"no-void": 2,
"no-warning-comments": 1,
"no-with": 2,
"prefer-destructuring": [
2,
{
"array": false,
"object": true
},
{
"enforceForRenamedProperties": false
}
],
"radix": 2,
"semi-spacing": 2,
"vars-on-top": 0,
"wrap-iife": 2,
"yoda": 2,
// Flow
"flowtype/use-flow-type": 2,
"flowtype/define-flow-type": 2,
"flowtype/space-after-type-colon": [
2,
"always"
],
"flowtype/space-before-type-colon": [
2,
"never"
],
"flowtype/require-valid-file-annotation": [
2,
"always",
{
"annotationStyle": "block"
}
],
// ES6
"arrow-body-style": 0,
"arrow-parens": 0,
"arrow-spacing": 2,
"constructor-super": 2,
"generator-star-spacing": [
2,
{
"before": true,
"after": true
}
],
"no-class-assign": 2,
"no-confusing-arrow": 2,
"no-const-assign": 2,
"no-dupe-class-members": 2,
"no-this-before-super": 2,
"no-var": 2,
"object-shorthand": 2,
"prefer-arrow-callback": 2,
"prefer-const": 2,
"prefer-reflect": 0,
"prefer-spread": 2,
"prefer-template": 2,
"rest-spread-spacing": [
2,
"never"
],
// Variables
"init-declarations": 0,
"no-catch-shadow": 2,
"no-delete-var": 2,
"no-label-var": 2,
"no-shadow": 0,
"no-shadow-restricted-names": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-undefined": 0,
"no-unused-vars": [
2,
{
"vars": "all",
"args": "none"
}
],
"no-use-before-define": 0,
"react/require-default-props": 0,
"react/prop-types": 0,
"react/jsx-filename-extension": [
1,
{
"extensions": [
".js",
".jsx"
]
}
],
"react/prefer-stateless-function": [
2,
{
"ignorePureComponents": true
}
],
"react/forbid-prop-types": [
0,
{
"forbid": []
}
],
"import/extensions": [
1,
"never",
{
"svg": "always"
}
],
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": true,
"optionalDependencies": false,
"peerDependencies": false
}
],
"semi": [
"error",
"never"
]
},
"env": {
"jest": true
}
}
}

17
.flowconfig Normal file
View File

@@ -0,0 +1,17 @@
[ignore]
.*/*[.]android.js
; Ignore unexpected extra "@providesModule"
.*/node_modules/.*/node_modules/fbjs/.*
[include]
[untyped]
.*/node_modules/react-native
[libs]
[version]
^0.83.0

View File

@@ -4,32 +4,14 @@
* @flow
*/
import React, { Component } from 'react';
import React from 'react'
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
} from 'react-native'
import ExampleMain from './ExampleMain'
class Example extends Component {
render() {
return (
<ExampleMain />
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
padding: 10
}
});
const Example = () => <ExampleMain />
AppRegistry.registerComponent('Example', () => Example);
AppRegistry.registerComponent('Example', () => Example)

View File

@@ -4,32 +4,13 @@
* @flow
*/
import React, { Component } from 'react';
import React from 'react'
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
} from 'react-native'
import ExampleMain from './ExampleMain'
class Example extends Component {
render() {
return (
<ExampleMain />
);
}
}
const Example = () => <ExampleMain />
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white',
padding: 10
}
});
AppRegistry.registerComponent('Example', () => Example);
AppRegistry.registerComponent('Example', () => Example)

View File

@@ -1,7 +1,41 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {View, ViewPropTypes, StyleSheet, Text} from 'react-native';
import TabOption from './TabOption';
/* @flow */
import React, { PureComponent } from 'react'
import {
View, StyleSheet,
} from 'react-native'
import type {
ViewStyleProp,
TextStyleProp,
} from 'react-native/Libraries/StyleSheet/StyleSheet'
import TabOption from './TabOption'
type Props = {
tabStyle: ViewStyleProp,
activeTabStyle: ViewStyleProp,
tabTextStyle: TextStyleProp,
activeTabTextStyle: TextStyleProp,
tabBadgeContainerStyle: TextStyleProp,
activeTabBadgeContainerStyle: TextStyleProp,
tabBadgeStyle: TextStyleProp,
activeTabBadgeStyle: TextStyleProp,
onTabPress: Function,
textNumberOfLines: number,
allowFontScaling: boolean,
accessible: boolean,
activeTabOpacity: number,
enabled: boolean,
values: string[],
badges: string[],
multiple: boolean,
selectedIndex: number,
selectedIndices: number[],
tabsContainerStyle: ViewStyleProp,
tabsContainerDisableStyle: ViewStyleProp,
borderRadius: number,
accessibilityLabels: string[],
}
const styles = StyleSheet.create({
tabsContainerStyle: {
@@ -17,47 +51,32 @@ const styles = StyleSheet.create({
borderWidth: 1,
backgroundColor: 'white',
},
});
const handleTabPress = (index, multiple, selectedIndex, onTabPress) => {
})
const handleTabPress = (
index: number,
multiple: boolean,
selectedIndex: number,
onTabPress: Function,
) => {
if (multiple) {
onTabPress(index);
onTabPress(index)
} else if (selectedIndex !== index) {
onTabPress(index);
onTabPress(index)
}
};
}
const getAccessibilityLabelByIndex = (accessibilityLabels, index) =>
accessibilityLabels &&
accessibilityLabels.length > 0 &&
accessibilityLabels[index]
? accessibilityLabels[index]
: undefined;
const getAccessibilityLabelByIndex = (
accessibilityLabels: string[],
index: number,
) => (accessibilityLabels
&& accessibilityLabels.length > 0
&& accessibilityLabels[index]
? accessibilityLabels[index]
: undefined)
export default class SegmentedControlTab extends PureComponent<Props> {
static defaultProps = {
export default class SegmentedControlTab extends PureComponent {
static propTypes = {
values: PropTypes.array,
badges: PropTypes.array,
multiple: PropTypes.bool,
onTabPress: PropTypes.func,
selectedIndex: PropTypes.number,
selectedIndices: PropTypes.arrayOf(PropTypes.number),
tabsContainerStyle: ViewPropTypes.style,
tabsContainerDisableStyle: ViewPropTypes.style,
tabStyle: ViewPropTypes.style,
activeTabStyle: ViewPropTypes.style,
tabTextStyle: Text.propTypes.style,
activeTabTextStyle: Text.propTypes.style,
tabBadgeContainerStyle: Text.propTypes.style,
activeTabBadgeContainerStyle: Text.propTypes.style,
tabBadgeStyle: Text.propTypes.style,
activeTabBadgeStyle: Text.propTypes.style,
borderRadius: PropTypes.number,
textNumberOfLines: PropTypes.number,
allowFontScaling: PropTypes.bool,
accessible: PropTypes.bool,
accessibilityLabels: PropTypes.array,
activeTabOpacity: PropTypes.number,
enabled: PropTypes.bool,
};
static defaultProps = {
@@ -70,7 +89,7 @@ export default class SegmentedControlTab extends PureComponent {
selectedIndices: [0],
onTabPress: () => {},
tabsContainerStyle: {},
tabsContainerDisableStyle: {opacity: 0.6},
tabsContainerDisableStyle: { opacity: 0.6 },
tabStyle: {},
activeTabStyle: {},
tabTextStyle: {},
@@ -86,6 +105,7 @@ export default class SegmentedControlTab extends PureComponent {
enabled: true,
};
render() {
const {
multiple,
@@ -111,36 +131,36 @@ export default class SegmentedControlTab extends PureComponent {
accessibilityLabels,
activeTabOpacity,
enabled,
} = this.props;
} = this.props
const firstTabStyle = [
{
borderRightWidth: values.length === 2 ? 1 : 0,
borderRightWidth: values && values.length === 2 ? 1 : 0,
borderTopLeftRadius: borderRadius,
borderBottomLeftRadius: borderRadius,
},
];
]
const lastTabStyle = [
{
borderLeftWidth: 0,
borderTopRightRadius: borderRadius,
borderBottomRightRadius: borderRadius,
},
];
]
const tabsContainerStyles = [styles.tabsContainerStyle, tabsContainerStyle];
const tabsContainerStyles = [styles.tabsContainerStyle, tabsContainerStyle]
if (!enabled) {
tabsContainerStyles.push(tabsContainerDisableStyle);
tabsContainerStyles.push(tabsContainerDisableStyle)
}
return (
<View style={tabsContainerStyles} removeClippedSubviews={false}>
{values.map((item, index) => {
{values && values.map((item, index) => {
const accessibilityText = getAccessibilityLabelByIndex(
accessibilityLabels,
index,
);
)
return (
<TabOption
key={`${index}${item}`}
key={item}
index={index}
badge={badges && badges[index] ? badges[index] : false}
isTabActive={
@@ -150,21 +170,20 @@ export default class SegmentedControlTab extends PureComponent {
}
text={item}
textNumberOfLines={textNumberOfLines}
onTabPress={indexs =>
handleTabPress(indexs, multiple, selectedIndex, onTabPress)
onTabPress={indexs => handleTabPress(indexs, multiple, selectedIndex, onTabPress)
}
firstTabStyle={
index === 0 ? [{borderRightWidth: 0}, firstTabStyle] : {}
index === 0 ? [{ borderRightWidth: 0 }, firstTabStyle] : {}
}
lastTabStyle={
index === values.length - 1
? [{borderLeftWidth: 0}, lastTabStyle]
? [{ borderLeftWidth: 0 }, lastTabStyle]
: {}
}
tabStyle={[
tabStyle,
index !== 0 && index !== values.length - 1
? {marginLeft: -1}
? { marginLeft: -1 }
: {},
]}
activeTabStyle={activeTabStyle}
@@ -180,9 +199,9 @@ export default class SegmentedControlTab extends PureComponent {
accessibilityLabel={accessibilityText || item}
enabled={enabled}
/>
);
)
})}
</View>
);
)
}
}

View File

@@ -1,12 +1,42 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
/* @flow */
import React, { PureComponent } from 'react'
import {
View,
TouchableOpacity,
StyleSheet,
Text,
ViewPropTypes
} from 'react-native';
StyleSheet,
Text,
} from 'react-native'
import type {
ViewStyleProp,
TextStyleProp,
} from 'react-native/Libraries/StyleSheet/StyleSheet'
type Props = {
isTabActive?: boolean,
index?: number,
badge?: any,
text: string,
firstTabStyle?: ViewStyleProp,
lastTabStyle?: ViewStyleProp,
tabStyle?: ViewStyleProp,
activeTabStyle?: ViewStyleProp,
tabTextStyle?: TextStyleProp,
activeTabTextStyle?: TextStyleProp,
tabBadgeContainerStyle?: TextStyleProp,
activeTabBadgeContainerStyle?: TextStyleProp,
tabBadgeStyle?: TextStyleProp,
activeTabBadgeStyle?: TextStyleProp,
onTabPress: Function,
textNumberOfLines?: number,
allowFontScaling?: boolean,
accessible?: boolean,
activeTabOpacity?: number,
accessibilityLabel?: string,
enabled?: boolean,
}
const styles = StyleSheet.create({
tabStyle: {
@@ -46,33 +76,9 @@ const styles = StyleSheet.create({
activeTabBadgeStyle: {
color: 'black',
},
});
export default class TabOption extends PureComponent {
static propTypes = {
isTabActive: PropTypes.bool,
index: PropTypes.number,
badge: PropTypes.any,
text: PropTypes.string.isRequired,
firstTabStyle: ViewPropTypes.style,
lastTabStyle: ViewPropTypes.style,
tabStyle: ViewPropTypes.style,
activeTabStyle: ViewPropTypes.style,
tabTextStyle: Text.propTypes.style,
activeTabTextStyle: Text.propTypes.style,
tabBadgeContainerStyle: Text.propTypes.style,
activeTabBadgeContainerStyle: Text.propTypes.style,
tabBadgeStyle: Text.propTypes.style,
activeTabBadgeStyle: Text.propTypes.style,
onTabPress: PropTypes.func,
textNumberOfLines: PropTypes.number,
allowFontScaling: PropTypes.bool,
accessible: PropTypes.any,
activeTabOpacity: PropTypes.number,
accessibilityLabel: PropTypes.string,
enabled: PropTypes.bool,
};
})
export default class TabOption extends PureComponent<Props> {
static defaultProps = {
isTabActive: false,
index: 0,
@@ -87,10 +93,9 @@ export default class TabOption extends PureComponent {
activeTabBadgeContainerStyle: {},
tabBadgeStyle: {},
activeTabBadgeStyle: {},
onTabPress() {},
textNumberOfLines: 1,
allowFontScaling: false,
accessible: {},
accessible: true,
activeTabOpacity: 1,
accessibilityLabel: '',
enabled: false,
@@ -119,7 +124,7 @@ export default class TabOption extends PureComponent {
activeTabOpacity,
accessibilityLabel,
enabled,
} = this.props;
} = this.props
return (
<TouchableOpacity
style={[
@@ -135,8 +140,9 @@ export default class TabOption extends PureComponent {
accessibilityComponentType="button"
onPress={() => onTabPress(index)}
disabled={!enabled}
activeOpacity={activeTabOpacity}>
<View style={{flexDirection: 'row'}}>
activeOpacity={activeTabOpacity}
>
<View style={{ flexDirection: 'row' }}>
<Text
style={[
styles.tabTextStyle,
@@ -147,7 +153,8 @@ export default class TabOption extends PureComponent {
]}
numberOfLines={textNumberOfLines}
allowFontScaling={allowFontScaling}
ellipsizeMode="tail">
ellipsizeMode="tail"
>
{text}
</Text>
{Boolean(badge) && (
@@ -157,11 +164,12 @@ export default class TabOption extends PureComponent {
tabBadgeContainerStyle,
isTabActive
? [
styles.activeTabBadgeContainerStyle,
activeTabBadgeContainerStyle,
]
styles.activeTabBadgeContainerStyle,
activeTabBadgeContainerStyle,
]
: {},
]}>
]}
>
<Text
style={[
styles.tabBadgeStyle,
@@ -170,13 +178,14 @@ export default class TabOption extends PureComponent {
? [styles.activeTabBadgeStyle, activeTabBadgeStyle]
: {},
]}
allowFontScaling={allowFontScaling}>
allowFontScaling={allowFontScaling}
>
{badge}
</Text>
</View>
)}
</View>
</TouchableOpacity>
);
)
}
}

8910
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
"description": "A react native component with the same concept of react native's SegmantedControlIOS, Primarily built to support both IOS and Android.",
"main": "SegmentedControlTab.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"flow": "flow check"
},
"repository": {
"type": "git",
@@ -26,7 +26,20 @@
"url": "https://github.com/kirankalyan5/react-native-segmented-control-tab/issues"
},
"homepage": "https://github.com/kirankalyan5/react-native-segmented-control-tab#readme",
"dependencies": {
"prop-types": "^15.5.10"
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^23.6.0",
"babel-preset-flow": "^6.23.0",
"babel-preset-react-native": "^4.0.1",
"eslint": "^5.7.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-flowtype": "^3.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.1.2",
"eslint-plugin-react": "^7.11.1",
"flow-bin": "^0.83.0",
"react": "16.6.0-alpha.8af6728",
"react-native": "^0.57.2"
}
}