Skip to content

Commit cdce6a5

Browse files
authored
feat: add SegmentedControl for android (#65)
* 1.3.0 * feat: add android component
1 parent 2f04938 commit cdce6a5

File tree

6 files changed

+227
-112
lines changed

6 files changed

+227
-112
lines changed

index.d.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
Constructor,
1111
} from 'react-native';
1212

13-
export interface SegmentedControlIOSProps extends ViewProps {
13+
export interface SegmentedControlProps extends ViewProps {
1414
/**
1515
* If false the user won't be able to interact with the control. Default value is true.
1616
*/
@@ -72,11 +72,11 @@ export interface SegmentedControlIOSProps extends ViewProps {
7272
* (iOS 13 only)
7373
* Overrides the control's appearance irrespective of the OS theme
7474
*/
75-
appearance?: 'dark' | 'light'
75+
appearance?: 'dark' | 'light';
7676
}
7777

7878
/**
79-
* Use `SegmentedControlIOS` to render a UISegmentedControl iOS.
79+
* Use `SegmentedControl` to render a UISegmentedControl iOS.
8080
*
8181
* #### Programmatically changing selected index
8282
*
@@ -86,7 +86,7 @@ export interface SegmentedControlIOSProps extends ViewProps {
8686
* selects a value and changes the index, as shown in the example below.
8787
*
8888
* ````
89-
* <SegmentedControlIOS
89+
* <SegmentedControl
9090
* values={['One', 'Two']}
9191
* selectedIndex={this.state.selectedIndex}
9292
* onChange={(event) => {
@@ -95,9 +95,10 @@ export interface SegmentedControlIOSProps extends ViewProps {
9595
* />
9696
* ````
9797
*/
98-
declare class SegmentedControlIOSComponent extends React.Component<
99-
SegmentedControlIOSProps
98+
99+
declare class SegmentedControlComponent extends React.Component<
100+
SegmentedControlProps
100101
> {}
101-
declare const SegmentedControlIOSBase: Constructor<NativeMethodsMixin> &
102-
typeof SegmentedControlIOSComponent;
103-
export default class SegmentedControlIOS extends SegmentedControlIOSBase {}
102+
declare const SegmentedControlBase: Constructor<NativeMethodsMixin> &
103+
typeof SegmentedControlComponent;
104+
export default class SegmentedControl extends SegmentedControlBase {}

js/RNCSegmentedControlNativeComponent.js

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,83 +5,14 @@
55
* LICENSE file in the root directory of this source tree.
66
*
77
* @flow
8-
* @format
98
*/
109
'use strict';
1110

1211
import {requireNativeComponent} from 'react-native';
13-
14-
import type {ViewProps} from 'react-native/Libraries/Components/View/ViewPropTypes';
15-
import type {SyntheticEvent} from 'react-native/Libraries/Types/CoreEventTypes';
1612
import type {NativeComponent} from 'react-native/Libraries/Renderer/shims/ReactNative';
17-
import type {ColorValue} from 'react-native/Libraries/StyleSheet/StyleSheetTypes';
18-
19-
type Event = SyntheticEvent<
20-
$ReadOnly<{|
21-
value: number,
22-
selectedSegmentIndex: number,
23-
|}>,
24-
>;
25-
26-
export type SegmentedControlIOSProps = $ReadOnly<{|
27-
...ViewProps,
28-
/**
29-
* The labels for the control's segment buttons, in order.
30-
*/
31-
values?: $ReadOnlyArray<string>,
32-
/**
33-
* The index in `props.values` of the segment to be (pre)selected.
34-
*/
35-
selectedIndex?: ?number,
36-
/**
37-
* Callback that is called when the user taps a segment;
38-
* passes the segment's value as an argument
39-
*/
40-
onValueChange?: ?(value: number) => mixed,
41-
/**
42-
* Callback that is called when the user taps a segment;
43-
* passes the event as an argument
44-
*/
45-
onChange?: ?(event: Event) => mixed,
46-
/**
47-
* If false the user won't be able to interact with the control.
48-
* Default value is true.
49-
*/
50-
enabled?: boolean,
51-
/**
52-
* Accent color of the control.
53-
*/
54-
tintColor?: ?ColorValue,
55-
/**
56-
* Text color of the control.
57-
* NOTE: this prop will only work for iOS >= 13
58-
*/
59-
textColor?: ?ColorValue,
60-
/**
61-
* Text color of the control when selected.
62-
* NOTE: this prop will only work for iOS >= 13
63-
*/
64-
activeTextColor?: ?ColorValue,
65-
/**
66-
* Background color of the control.
67-
* NOTE: this prop will only work for iOS >= 13
68-
*/
69-
backgroundColor?: ?ColorValue,
70-
/**
71-
* If true, then selecting a segment won't persist visually.
72-
* The `onValueChange` callback will still work as expected.
73-
*/
74-
momentary?: ?boolean,
75-
/**
76-
* (iOS 13 only)
77-
* Overrides the control's appearance irrespective of the OS theme
78-
*/
79-
appearance?: 'dark' | 'light',
80-
|}>;
13+
import type {SegmentedControlProps} from './types';
8114

82-
type NativeSegmentedControlIOS = Class<
83-
NativeComponent<SegmentedControlIOSProps>,
84-
>;
15+
type NativeSegmentedControlIOS = Class<NativeComponent<SegmentedControlProps>>;
8516

8617
module.exports = ((requireNativeComponent(
8718
'RNCSegmentedControl',

js/SegmentedControlIOS.android.js

Lines changed: 63 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,77 @@
11
/**
2-
* Copyright (c) Facebook, Inc. and its affiliates.
3-
*
4-
* This source code is licensed under the MIT license found in the
5-
* LICENSE file in the root directory of this source tree.
6-
*
72
* @flow
83
*/
94

105
'use strict';
116

127
import * as React from 'react';
13-
import {StyleSheet, Text, View} from 'react-native';
8+
import {StyleSheet, View} from 'react-native';
9+
import {SegmentedControlTab} from './SegmentedControlTab';
1410

15-
const DummySegmentedControlIOS = (props: *) => (
16-
<View style={[styles.dummy, props.style]}>
17-
<Text style={styles.text}>
18-
SegmentedControlIOS is not supported on this platform!
19-
</Text>
20-
</View>
21-
);
11+
import type {SegmentedControlProps} from './types';
12+
const SegmentedControl = ({
13+
style,
14+
onChange,
15+
onValueChange,
16+
enabled = true,
17+
selectedIndex,
18+
activeTextColor,
19+
values,
20+
tintColor,
21+
textColor,
22+
backgroundColor,
23+
}: SegmentedControlProps) => {
24+
const handleChange = (index: number) => {
25+
// mocks iOS's nativeEvent
26+
const event = {
27+
nativeEvent: {
28+
value: values[index],
29+
selectedSegmentIndex: index,
30+
},
31+
};
32+
onChange && onChange(event);
33+
onValueChange && onValueChange(values[index]);
34+
};
35+
return (
36+
<View
37+
style={[
38+
styles.default,
39+
style,
40+
backgroundColor && {backgroundColor},
41+
!enabled && styles.disabled,
42+
]}>
43+
{values.map((value, index) => {
44+
return (
45+
<SegmentedControlTab
46+
enabled={enabled}
47+
selected={selectedIndex === index}
48+
key={index}
49+
value={value}
50+
tintColor={tintColor}
51+
textColor={textColor}
52+
activeTextColor={activeTextColor}
53+
onSelect={() => {
54+
handleChange(index);
55+
}}
56+
/>
57+
);
58+
})}
59+
</View>
60+
);
61+
};
2262

2363
const styles = StyleSheet.create({
24-
dummy: {
25-
width: 120,
26-
height: 50,
27-
backgroundColor: '#ffbcbc',
28-
borderWidth: 1,
29-
borderColor: 'red',
30-
alignItems: 'center',
31-
justifyContent: 'center',
64+
default: {
65+
flexDirection: 'row',
66+
justifyContent: 'space-evenly',
67+
alignContent: 'center',
68+
height: 28,
69+
backgroundColor: '#eee',
70+
borderRadius: 5,
3271
},
33-
text: {
34-
color: '#333333',
35-
margin: 5,
36-
fontSize: 10,
72+
disabled: {
73+
opacity: 0.4,
3774
},
3875
});
3976

40-
export default DummySegmentedControlIOS;
77+
export default SegmentedControl;

js/SegmentedControlIOS.ios.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,8 @@ import {StyleSheet} from 'react-native';
1414

1515
import type {SyntheticEvent} from 'react-native/Libraries/Types/CoreEventTypes';
1616

17-
import RNCSegmentedControlNativeComponent, {
18-
type SegmentedControlIOSProps,
19-
} from './RNCSegmentedControlNativeComponent';
17+
import RNCSegmentedControlNativeComponent from './RNCSegmentedControlNativeComponent';
18+
import type {SegmentedControlProps} from './types';
2019

2120
type Event = SyntheticEvent<
2221
$ReadOnly<{|
@@ -26,7 +25,7 @@ type Event = SyntheticEvent<
2625
>;
2726

2827
type Props = $ReadOnly<{|
29-
...SegmentedControlIOSProps,
28+
...SegmentedControlProps,
3029
forwardedRef: ?React.Ref<typeof RNCSegmentedControlNativeComponent>,
3130
|}>;
3231

@@ -83,11 +82,11 @@ const styles = StyleSheet.create({
8382
});
8483

8584
const SegmentedControlIOSWithRef = React.forwardRef<
86-
SegmentedControlIOSProps,
85+
SegmentedControlProps,
8786
RNCSegmentedControlNativeComponent,
8887
>(
8988
(
90-
props: SegmentedControlIOSProps,
89+
props: SegmentedControlProps,
9190
forwardedRef: ?React.Ref<typeof RNCSegmentedControlNativeComponent>,
9291
) => {
9392
return <SegmentedControlIOS {...props} forwardedRef={forwardedRef} />;

js/SegmentedControlTab.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* @flow
3+
*/
4+
5+
'use strict';
6+
7+
import * as React from 'react';
8+
import {StyleSheet, View, Text, TouchableOpacity} from 'react-native';
9+
10+
type Props = $ReadOnly<{|
11+
value: string,
12+
tintColor?: string,
13+
textColor?: string,
14+
activeTextColor?: string,
15+
onSelect: () => void,
16+
selected: Boolean,
17+
enabled: Boolean,
18+
|}>;
19+
20+
export const SegmentedControlTab = ({
21+
onSelect,
22+
value,
23+
enabled,
24+
selected,
25+
tintColor,
26+
textColor,
27+
activeTextColor,
28+
}: Props) => {
29+
const getColor = () => {
30+
if (selected && activeTextColor) {
31+
return activeTextColor;
32+
}
33+
if (textColor) {
34+
return textColor;
35+
}
36+
if (tintColor) {
37+
return tintColor;
38+
}
39+
return 'black';
40+
};
41+
const color = getColor();
42+
43+
const getBackgroundColor = () => {
44+
if (selected && tintColor) {
45+
return tintColor;
46+
}
47+
return 'white';
48+
};
49+
return (
50+
<TouchableOpacity
51+
style={styles.container}
52+
disabled={!enabled}
53+
onPress={onSelect}>
54+
<View
55+
style={[
56+
styles.default,
57+
selected && {backgroundColor: getBackgroundColor()},
58+
]}>
59+
<Text style={[{color}, selected && styles.activeText]}>{value}</Text>
60+
</View>
61+
</TouchableOpacity>
62+
);
63+
};
64+
65+
const styles = StyleSheet.create({
66+
container: {flex: 1, borderRadius: 5},
67+
default: {
68+
flex: 1,
69+
justifyContent: 'center',
70+
alignItems: 'center',
71+
margin: 2,
72+
borderRadius: 5,
73+
},
74+
activeText: {
75+
fontWeight: '700',
76+
},
77+
});

0 commit comments

Comments
 (0)