mirror of
https://github.com/expo/react-native-action-sheet.git
synced 2026-11-14 19:11:42 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
040980edcf | ||
|
|
231630b875 | ||
|
|
3c2ccec2d1 | ||
|
|
e35a4bffbb | ||
|
|
c87602ea3b | ||
|
|
8c1a319a3a | ||
|
|
ac17d305db | ||
|
|
2b9d8e5ec6 | ||
|
|
fe8df1b685 | ||
|
|
74473dc4bd | ||
|
|
2ca8d6217e | ||
|
|
cebaef95a3 | ||
|
|
15460b6a08 | ||
|
|
afb0fefd7b | ||
|
|
04a99ae94b | ||
|
|
432ae92569 | ||
|
|
2fa0e21086 | ||
|
|
33d77f4d04 | ||
|
|
883399be33 | ||
|
|
59e34c83b4 | ||
|
|
2e75f9551d | ||
|
|
42740e93a0 | ||
|
|
3c95b8a86d | ||
|
|
f35de5dd25 | ||
|
|
8ef0ee36e7 | ||
|
|
3a9bc50e84 | ||
|
|
7fe0aff0fe | ||
|
|
98528d9399 | ||
|
|
8ec19d3bfc | ||
|
|
3e2a8d6c1d | ||
|
|
81f64772c7 | ||
|
|
fa027c0f81 | ||
|
|
f9376eb0ca | ||
|
|
72d2e2a0ea | ||
|
|
acfd89c465 | ||
|
|
b2862ba08c | ||
|
|
dfe724247c | ||
|
|
150cf5fcca | ||
|
|
b25206f7e6 | ||
|
|
ccfc3b448a | ||
|
|
38150163c1 | ||
|
|
88d27eba92 | ||
|
|
9fee7dc244 |
No files matched your search
+183
-73
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import * as React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Animated,
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
Image,
|
||||
NativeModules,
|
||||
TouchableOpacity,
|
||||
TouchableNativeFeedback,
|
||||
TouchableWithoutFeedback,
|
||||
@@ -20,20 +19,36 @@ import {
|
||||
|
||||
type ActionSheetOptions = {
|
||||
options: Array<string>,
|
||||
icons: ?Array<number>,
|
||||
destructiveButtonIndex: ?number,
|
||||
cancelButtonIndex: ?number,
|
||||
textStyle: ?any,
|
||||
icons?: ?Array<number | React.Node>,
|
||||
tintIcons?: ?boolean,
|
||||
destructiveButtonIndex?: ?number,
|
||||
cancelButtonIndex?: ?number,
|
||||
textStyle?: ?any,
|
||||
tintColor?: ?string,
|
||||
title?: ?string,
|
||||
titleTextStyle?: ?any,
|
||||
message?: ?string,
|
||||
messageTextStyle?: ?any,
|
||||
showSeparators?: ?boolean,
|
||||
separatorStyle?: ?any,
|
||||
};
|
||||
|
||||
type ActionGroupProps = {
|
||||
options: Array<string>,
|
||||
icons: ?Array<number>,
|
||||
icons: ?Array<number | React.Node>,
|
||||
tintIcons: ?boolean,
|
||||
destructiveButtonIndex: ?number,
|
||||
onSelect: (i: number) => boolean,
|
||||
startIndex: number,
|
||||
length: number,
|
||||
textStyle: ?any,
|
||||
tintColor: ?string,
|
||||
title: ?string,
|
||||
titleTextStyle: ?any,
|
||||
message: ?string,
|
||||
messageTextStyle: ?any,
|
||||
showSeparators: ?boolean,
|
||||
separatorStyle: ?any,
|
||||
};
|
||||
|
||||
type ActionSheetState = {
|
||||
@@ -52,31 +67,27 @@ type ActionSheetProps = {
|
||||
|
||||
const OPACITY_ANIMATION_IN_TIME = 225;
|
||||
const OPACITY_ANIMATION_OUT_TIME = 195;
|
||||
const EASING_OUT = Easing.bezier(0.25, 0.46, 0.45, 0.94)
|
||||
const EASING_IN = Easing.out(EASING_OUT)
|
||||
const EASING_OUT = Easing.bezier(0.25, 0.46, 0.45, 0.94);
|
||||
const EASING_IN = Easing.out(EASING_OUT);
|
||||
const BLACK_54PC_TRANSPARENT = '#0000008a';
|
||||
const BLACK_87PC_TRANSPARENT = '#000000de';
|
||||
const DESTRUCTIVE_COLOR = '#d32f2f';
|
||||
|
||||
class ActionGroup extends React.Component {
|
||||
props: ActionGroupProps;
|
||||
|
||||
static propTypes = {
|
||||
options: PropTypes.array.isRequired,
|
||||
icons: PropTypes.array,
|
||||
destructiveButtonIndex: PropTypes.number,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
startIndex: PropTypes.number.isRequired,
|
||||
length: PropTypes.number.isRequired,
|
||||
textStyle: Text.propTypes.style,
|
||||
};
|
||||
|
||||
render() {
|
||||
let {
|
||||
options,
|
||||
icons,
|
||||
tintIcons,
|
||||
destructiveButtonIndex,
|
||||
onSelect,
|
||||
startIndex,
|
||||
length,
|
||||
textStyle,
|
||||
tintColor,
|
||||
showSeparators,
|
||||
} = this.props;
|
||||
|
||||
let optionViews = [];
|
||||
@@ -87,19 +98,24 @@ class ActionGroup extends React.Component {
|
||||
);
|
||||
|
||||
for (let i = startIndex; i < startIndex + length; i++) {
|
||||
let color = '#212121';
|
||||
if (i === destructiveButtonIndex) {
|
||||
color = '#d32f2f';
|
||||
}
|
||||
|
||||
const defaultColor = tintColor
|
||||
? tintColor
|
||||
: (textStyle || {}).color || BLACK_87PC_TRANSPARENT;
|
||||
const color = i === destructiveButtonIndex ? DESTRUCTIVE_COLOR : defaultColor;
|
||||
const iconSource = icons != null && icons[i];
|
||||
let iconElement = undefined;
|
||||
|
||||
if (icons && icons[i]) {
|
||||
const iconStyle = [styles.icon]
|
||||
if (textStyle.color !== undefined && textStyle.color !== null) {
|
||||
iconStyle.push({ tintColor: textStyle.color })
|
||||
if (iconSource) {
|
||||
if (typeof iconSource === 'number') {
|
||||
const iconStyle = [styles.icon, { tintColor: tintIcons ? color : null }];
|
||||
iconElement = <Image
|
||||
fadeDuration={0}
|
||||
source={iconSource}
|
||||
resizeMode="contain"
|
||||
style={iconStyle}/>;
|
||||
} else {
|
||||
iconElement = <View style={styles.icon}>{iconSource}</View>;
|
||||
}
|
||||
iconElement = <Image source={icons[i]} resizeMode="contain" style={iconStyle} />;
|
||||
}
|
||||
|
||||
optionViews.push(
|
||||
@@ -110,26 +126,76 @@ class ActionGroup extends React.Component {
|
||||
onPress={() => onSelect(i)}
|
||||
style={styles.button}>
|
||||
{iconElement}
|
||||
<Text style={[styles.text, { color }, textStyle]}>{options[i]}</Text>
|
||||
<Text style={[styles.text, textStyle, { color }]}>{options[i]}</Text>
|
||||
</TouchableNativeFeedbackSafe>
|
||||
);
|
||||
|
||||
if (i < startIndex + length - 1) {
|
||||
optionViews.push(<View key={`separator-${i}`} style={styles.rowSeparator} />);
|
||||
if (showSeparators && i < startIndex + length - 1) {
|
||||
optionViews.push(this._renderRowSeparator(i));
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.groupContainer}>
|
||||
{this._renderTitleContent()}
|
||||
<ScrollView>{optionViews}</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_renderRowSeparator(key) {
|
||||
return <View key={key ? `separator-${key}` : null} style={[styles.rowSeparator, this.props.separatorStyle]} />;
|
||||
}
|
||||
|
||||
_renderTitleContent() {
|
||||
const { title, titleTextStyle, message, messageTextStyle, showSeparators } = this.props;
|
||||
|
||||
if (!title && !message) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View>
|
||||
<View style={[styles.titleContainer, { paddingBottom: showSeparators ? 24 : 16 }]}>
|
||||
{!!title && <Text style={[styles.title, titleTextStyle]}>{title}</Text>}
|
||||
{!!message && <Text style={[styles.message, messageTextStyle]}>{message}</Text>}
|
||||
</View>
|
||||
{!!showSeparators && this._renderRowSeparator('title')}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ActionGroup.propTypes = {
|
||||
options: PropTypes.array.isRequired,
|
||||
icons: PropTypes.array,
|
||||
tintIcons: PropTypes.bool,
|
||||
destructiveButtonIndex: PropTypes.number,
|
||||
onSelect: PropTypes.func.isRequired,
|
||||
startIndex: PropTypes.number.isRequired,
|
||||
length: PropTypes.number.isRequired,
|
||||
textStyle: Text.propTypes.style,
|
||||
tintColor: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
titleTextStyle: Text.propTypes.style,
|
||||
message: PropTypes.string,
|
||||
messageTextStyle: Text.propTypes.style,
|
||||
showSeparators: PropTypes.bool,
|
||||
separatorStyle: PropTypes.object,
|
||||
};
|
||||
|
||||
ActionGroup.defaultProps = {
|
||||
title: null,
|
||||
message: null,
|
||||
showSeparators: false,
|
||||
tintIcons: true,
|
||||
textStyle: {},
|
||||
};
|
||||
|
||||
// Has same API as https://facebook.github.io/react-native/docs/actionsheetios.html
|
||||
export default class ActionSheet extends React.Component {
|
||||
props: ActionSheetProps;
|
||||
|
||||
_actionSheetHeight = 360;
|
||||
_animateOutCallback: ?() => void = null;
|
||||
|
||||
@@ -142,48 +208,64 @@ export default class ActionSheet extends React.Component {
|
||||
sheetOpacity: new Animated.Value(0),
|
||||
};
|
||||
|
||||
_setActionSheetHeight = ({ nativeEvent }) =>
|
||||
this._actionSheetHeight = nativeEvent.layout.height;
|
||||
|
||||
render() {
|
||||
let { isVisible } = this.state;
|
||||
let overlay = isVisible ? (
|
||||
const { isVisible, overlayOpacity } = this.state;
|
||||
const overlay = !!isVisible && (
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.overlay,
|
||||
{
|
||||
opacity: this.state.overlayOpacity,
|
||||
opacity: overlayOpacity,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
) : null;
|
||||
|
||||
let sheet = isVisible ? this._renderSheet() : null;
|
||||
);
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1 }}>
|
||||
{React.Children.only(this.props.children)}
|
||||
{overlay}
|
||||
{sheet}
|
||||
{!!isVisible && this._renderSheet()}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_renderSheet() {
|
||||
if (!this.state.options) {
|
||||
return;
|
||||
const { options, isAnimating, sheetOpacity } = this.state;
|
||||
|
||||
if (!options) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let numOptions = this.state.options.options.length;
|
||||
const {
|
||||
options: optionsArray,
|
||||
icons,
|
||||
tintIcons,
|
||||
destructiveButtonIndex,
|
||||
textStyle,
|
||||
tintColor,
|
||||
title,
|
||||
titleTextStyle,
|
||||
message,
|
||||
messageTextStyle,
|
||||
showSeparators,
|
||||
separatorStyle,
|
||||
} = options;
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={this._selectCancelButton}>
|
||||
<Animated.View
|
||||
needsOffscreenAlphaCompositing={this.state.isAnimating}
|
||||
needsOffscreenAlphaCompositing={isAnimating}
|
||||
style={[
|
||||
styles.sheetContainer,
|
||||
{
|
||||
opacity: this.state.sheetOpacity,
|
||||
opacity: sheetOpacity,
|
||||
transform: [
|
||||
{
|
||||
translateY: this.state.sheetOpacity.interpolate({
|
||||
translateY: sheetOpacity.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [this._actionSheetHeight, 0],
|
||||
}),
|
||||
@@ -191,16 +273,23 @@ export default class ActionSheet extends React.Component {
|
||||
],
|
||||
},
|
||||
]}>
|
||||
<View style={styles.sheet}
|
||||
onLayout={(event) => { this._actionSheetHeight = event.nativeEvent.layout.height }}>
|
||||
<View style={styles.sheet} onLayout={this._setActionSheetHeight}>
|
||||
<ActionGroup
|
||||
options={this.state.options.options}
|
||||
icons={this.state.options.icons}
|
||||
destructiveButtonIndex={this.state.options.destructiveButtonIndex}
|
||||
options={optionsArray}
|
||||
icons={icons}
|
||||
tintIcons={tintIcons === undefined ? true : tintIcons}
|
||||
destructiveButtonIndex={destructiveButtonIndex}
|
||||
onSelect={this._onSelect}
|
||||
startIndex={0}
|
||||
length={numOptions}
|
||||
textStyle={this.state.options.textStyle}
|
||||
length={optionsArray.length}
|
||||
textStyle={textStyle || {}}
|
||||
tintColor={tintColor}
|
||||
title={title || null}
|
||||
titleTextStyle={titleTextStyle}
|
||||
message={message || null}
|
||||
messageTextStyle={messageTextStyle}
|
||||
showSeparators={showSeparators}
|
||||
separatorStyle={separatorStyle}
|
||||
/>
|
||||
</View>
|
||||
</Animated.View>
|
||||
@@ -213,7 +302,9 @@ export default class ActionSheet extends React.Component {
|
||||
onSelect: (i: number) => void,
|
||||
onAnimateOut: () => void
|
||||
) {
|
||||
if (this.state.isVisible) {
|
||||
const { isVisible, overlayOpacity, sheetOpacity } = this.state;
|
||||
|
||||
if (isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -224,17 +315,17 @@ export default class ActionSheet extends React.Component {
|
||||
isAnimating: true,
|
||||
});
|
||||
|
||||
this.state.overlayOpacity.setValue(0);
|
||||
this.state.sheetOpacity.setValue(0);
|
||||
overlayOpacity.setValue(0);
|
||||
sheetOpacity.setValue(0);
|
||||
|
||||
Animated.parallel([
|
||||
Animated.timing(this.state.overlayOpacity, {
|
||||
toValue: 0.2,
|
||||
Animated.timing(overlayOpacity, {
|
||||
toValue: 0.32,
|
||||
easing: EASING_OUT,
|
||||
duration: OPACITY_ANIMATION_IN_TIME,
|
||||
useNativeDriver: this.props.useNativeDriver,
|
||||
}),
|
||||
Animated.timing(this.state.sheetOpacity, {
|
||||
Animated.timing(sheetOpacity, {
|
||||
toValue: 1,
|
||||
easing: EASING_OUT,
|
||||
duration: OPACITY_ANIMATION_IN_TIME,
|
||||
@@ -257,28 +348,33 @@ export default class ActionSheet extends React.Component {
|
||||
}
|
||||
|
||||
_selectCancelButton = () => {
|
||||
if (!this.state.options) {
|
||||
const { options } = this.state;
|
||||
if (!options) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof this.state.options.cancelButtonIndex === 'number') {
|
||||
return this._onSelect(this.state.options.cancelButtonIndex);
|
||||
if (typeof options.cancelButtonIndex === 'number') {
|
||||
return this._onSelect(options.cancelButtonIndex);
|
||||
} else {
|
||||
return this._animateOut();
|
||||
}
|
||||
};
|
||||
|
||||
_onSelect = (index: number): boolean => {
|
||||
if (this.state.isAnimating) {
|
||||
const { isAnimating, onSelect } = this.state;
|
||||
|
||||
if (isAnimating) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.state.onSelect && this.state.onSelect(index);
|
||||
onSelect && onSelect(index);
|
||||
return this._animateOut();
|
||||
};
|
||||
|
||||
_animateOut = (): boolean => {
|
||||
if (this.state.isAnimating) {
|
||||
const { isAnimating, overlayOpacity, sheetOpacity } = this.state;
|
||||
|
||||
if (isAnimating) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -292,13 +388,13 @@ export default class ActionSheet extends React.Component {
|
||||
});
|
||||
|
||||
Animated.parallel([
|
||||
Animated.timing(this.state.overlayOpacity, {
|
||||
Animated.timing(overlayOpacity, {
|
||||
toValue: 0,
|
||||
easing: EASING_IN,
|
||||
duration: OPACITY_ANIMATION_OUT_TIME,
|
||||
useNativeDriver: this.props.useNativeDriver,
|
||||
}),
|
||||
Animated.timing(this.state.sheetOpacity, {
|
||||
Animated.timing(sheetOpacity, {
|
||||
toValue: 0,
|
||||
easing: EASING_IN,
|
||||
duration: OPACITY_ANIMATION_OUT_TIME,
|
||||
@@ -353,19 +449,16 @@ class TouchableNativeFeedbackSafe extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
let styles = StyleSheet.create({
|
||||
const styles = StyleSheet.create({
|
||||
groupContainer: {
|
||||
backgroundColor: '#fefefe',
|
||||
borderColor: '#ffffff',
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
backgroundColor: '#ffffff',
|
||||
overflow: 'hidden',
|
||||
paddingVertical: 8,
|
||||
},
|
||||
button: {
|
||||
justifyContent: 'flex-start',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
height: 48,
|
||||
height: 56,
|
||||
paddingHorizontal: 16,
|
||||
},
|
||||
icon: {
|
||||
@@ -375,12 +468,13 @@ let styles = StyleSheet.create({
|
||||
},
|
||||
text: {
|
||||
fontSize: 16,
|
||||
color: BLACK_87PC_TRANSPARENT,
|
||||
textAlignVertical: 'center',
|
||||
},
|
||||
rowSeparator: {
|
||||
backgroundColor: '#dddddd',
|
||||
height: 1,
|
||||
flex: 1,
|
||||
width: '100%',
|
||||
},
|
||||
overlay: {
|
||||
position: 'absolute',
|
||||
@@ -405,4 +499,20 @@ let styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
titleContainer: {
|
||||
alignItems: 'flex-start',
|
||||
padding: 16,
|
||||
paddingTop: 24,
|
||||
},
|
||||
title: {
|
||||
fontSize: 16,
|
||||
color: BLACK_54PC_TRANSPARENT,
|
||||
textAlignVertical: 'center',
|
||||
},
|
||||
message: {
|
||||
marginTop: 12,
|
||||
fontSize: 14,
|
||||
color: BLACK_54PC_TRANSPARENT,
|
||||
textAlignVertical: 'center',
|
||||
},
|
||||
});
|
||||
+9
-1
@@ -9,6 +9,14 @@ export default class ActionSheet extends React.Component {
|
||||
}
|
||||
|
||||
showActionSheetWithOptions(options, onSelect) {
|
||||
ActionSheetIOS.showActionSheetWithOptions(options, onSelect);
|
||||
const iosOptions = {
|
||||
...options,
|
||||
// A null title or message on iOS causes a crash
|
||||
title: options.title || undefined,
|
||||
message: options.message || undefined,
|
||||
icons: undefined,
|
||||
};
|
||||
|
||||
ActionSheetIOS.showActionSheetWithOptions(iosOptions, onSelect);
|
||||
}
|
||||
}
|
||||
+36
-3
@@ -4,17 +4,50 @@ import ActionSheet from './ActionSheet';
|
||||
|
||||
export default class ActionSheetProvider extends React.Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.any,
|
||||
textStyle: PropTypes.object,
|
||||
tintIcons: PropTypes.bool,
|
||||
tintColor: PropTypes.string,
|
||||
titleTextStyle: PropTypes.object,
|
||||
messageTextStyle: PropTypes.object,
|
||||
showSeparators: PropTypes.bool,
|
||||
separatorStyle: PropTypes.object,
|
||||
children: PropTypes.any.isRequired,
|
||||
};
|
||||
|
||||
static childContextTypes = {
|
||||
showActionSheetWithOptions: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
textStyle: {},
|
||||
tintIcons: true,
|
||||
titleTextStyle: {},
|
||||
messageTextStyle: {},
|
||||
showSeparators: false,
|
||||
separatorStyle: {},
|
||||
};
|
||||
|
||||
getNextValue = (config, propKey) => config[propKey] !== undefined ? config[propKey] : this.props[propKey];
|
||||
|
||||
getNextObject = (config, propKey) => ({...this.props[propKey], ...config[propKey]});
|
||||
|
||||
getChildContext() {
|
||||
return {
|
||||
showActionSheetWithOptions: (...args) =>
|
||||
this._actionSheetRef.showActionSheetWithOptions(...args),
|
||||
showActionSheetWithOptions: (...args) => {
|
||||
const [config, ...rest] = args;
|
||||
const nextConfig = {
|
||||
...config,
|
||||
textStyle: this.getNextObject(config, 'textStyle'),
|
||||
tintIcons: this.getNextValue(config, 'tintIcons'),
|
||||
tintColor: this.getNextValue(config, 'tintColor'),
|
||||
titleTextStyle: this.getNextObject(config, 'titleTextStyle'),
|
||||
messageTextStyle: this.getNextObject(config, 'messageTextStyle'),
|
||||
showSeparators: this.getNextValue(config, 'showSeparators'),
|
||||
separatorStyle: this.getNextObject(config, 'separatorStyle'),
|
||||
};
|
||||
|
||||
this._actionSheetRef.showActionSheetWithOptions(nextConfig, ...rest);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -41,9 +41,9 @@ access actionSheet method as `this.props.showActionSheetWithOptions`
|
||||
```es6
|
||||
_onOpenActionSheet = () => {
|
||||
// Same interface as https://facebook.github.io/react-native/docs/actionsheetios.html
|
||||
let options = ['Delete', 'Save', 'Cancel'];
|
||||
let destructiveButtonIndex = 0;
|
||||
let cancelButtonIndex = 2;
|
||||
const options = ['Delete', 'Save', 'Cancel'];
|
||||
const destructiveButtonIndex = 0;
|
||||
const cancelButtonIndex = 2;
|
||||
|
||||
this.props.showActionSheetWithOptions({
|
||||
options,
|
||||
@@ -53,10 +53,28 @@ _onOpenActionSheet = () => {
|
||||
(buttonIndex) => {
|
||||
// Do something here depending on the button index selected
|
||||
});
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
The goal of this library is to mimic the native iOS and Android ActionSheets as closely as possible.
|
||||
|
||||
### Universal Props
|
||||
|
||||
The same options available on React Native's [ActionSheetIOS](https://facebook.github.io/react-native/docs/actionsheetios.html#showactionsheetwithoptions) component exist for both iOS and Android in this library.
|
||||
|
||||
### Android-Only Props
|
||||
|
||||
The below props allow modification of the Android ActionSheet. They have no effect on the look on iOS as the native iOS Action Sheet does not have options for modifying these options.
|
||||
|
||||
* `icons` (array of required images or icons; optional): Show icons to go along with each option. If image source paths are provided via `require`, images will be rendered for you. Alternatively, you can provide an array of elements such as vector icons, pre-rendered Images, etc.
|
||||
* `tintIcons` (boolean; optional; default: `true`): Icons by default will be tinted to match the text color. When set to false, the icons will be the color of the source image. This is useful if you want to use multicolor icons. If you provide your own nodes/pre-rendered icons rather than required images in the `icons` array, you will need to tint them appropriately before providing them in the array of `icons`; `tintColor` will not be applied to icons unless they are images from a required source.
|
||||
* `textStyle` (Text.propTypes.style; optional): Apply any text style props to the options. If the `tintColor` option is provided, it takes precedence over a color text style prop.
|
||||
* `titleTextStyle` (Text.propTypes.style; optional): Apply any text style props to the title if present.
|
||||
* `messageTextStyle` (Text.propTypes.style; optional): Apply any text style props to the message if present.
|
||||
* `showSeparators`: (boolean; optional; default: false): Show separators between items. On iOS, separators always show so this prop has no effect.
|
||||
* `separatorStyle`: (View.propTypes.style; optional): Modify the look of the separators rather than use the default look.
|
||||
|
||||
## Try it out
|
||||
|
||||
@@ -64,4 +82,4 @@ Try it in Expo: https://expo.io/@community/react-native-action-sheet-example
|
||||
|
||||
## Usage
|
||||
|
||||
[See the example app source](https://github.com/expo/react-native-action-sheet/tree/master/example)
|
||||
See the [example app](https://github.com/expo/react-native-action-sheet/tree/master/example) for examples of how to apply different options.
|
||||
+114
-34
@@ -1,8 +1,8 @@
|
||||
import Expo from 'expo';
|
||||
import React from 'react';
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import { Entypo } from '@expo/vector-icons';
|
||||
import { StyleSheet, Text, View, ScrollView, SafeAreaView } from 'react-native';
|
||||
import { ActionSheetProvider, connectActionSheet } from '@expo/react-native-action-sheet';
|
||||
import ShowActionSheetButton from './ShowActionSheetButton';
|
||||
|
||||
export default class AppContainer extends React.Component {
|
||||
render() {
|
||||
@@ -16,45 +16,125 @@ export default class AppContainer extends React.Component {
|
||||
|
||||
@connectActionSheet
|
||||
class App extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<View style={{ marginBottom: 30 }}>
|
||||
<Text style={{ textAlign: 'center' }}>
|
||||
Hello! This is a simple example app to demonstrate @expo/react-native-action-sheet.
|
||||
</Text>
|
||||
</View>
|
||||
state = {
|
||||
selectedIndex: null,
|
||||
};
|
||||
|
||||
<Entypo.Button name="code" backgroundColor="#3e3e3e" onPress={this._onOpenActionSheet}>
|
||||
<Text style={{ fontSize: 15, color: '#fff' }}>Open action sheet</Text>
|
||||
</Entypo.Button>
|
||||
</View>
|
||||
);
|
||||
_updateSelectionText = (selectedIndex) => {
|
||||
this.setState({ selectedIndex });
|
||||
};
|
||||
|
||||
_renderSelectionText() {
|
||||
const { selectedIndex } = this.state;
|
||||
const text = selectedIndex === null
|
||||
? 'No Option Selected'
|
||||
: `Option #${selectedIndex + 1} Selected`;
|
||||
|
||||
return <Text style={styles.selectionText}>{text}</Text>
|
||||
}
|
||||
|
||||
_onOpenActionSheet = () => {
|
||||
// Same interface as https://facebook.github.io/react-native/docs/actionsheetios.html
|
||||
let options = ['Delete', 'Save', 'Cancel'];
|
||||
let destructiveButtonIndex = 0;
|
||||
let cancelButtonIndex = 2;
|
||||
this.props.showActionSheetWithOptions(
|
||||
{
|
||||
options,
|
||||
cancelButtonIndex,
|
||||
destructiveButtonIndex,
|
||||
},
|
||||
buttonIndex => {
|
||||
// Do something here depending on the button index selected
|
||||
}
|
||||
_renderSectionHeader(text) {
|
||||
return <Text style={styles.sectionHeaderText}>{text}</Text>;
|
||||
}
|
||||
|
||||
_renderButtons() {
|
||||
const { showActionSheetWithOptions } = this.props;
|
||||
return (
|
||||
<View style={{ alignItems: 'center' }}>
|
||||
{this._renderSectionHeader('Universal Options')}
|
||||
<ShowActionSheetButton
|
||||
title="Options Only"
|
||||
onSelection={this._updateSelectionText}
|
||||
showActionSheetWithOptions={showActionSheetWithOptions} />
|
||||
<ShowActionSheetButton
|
||||
title="Title"
|
||||
withTitle
|
||||
onSelection={this._updateSelectionText}
|
||||
showActionSheetWithOptions={showActionSheetWithOptions} />
|
||||
<ShowActionSheetButton
|
||||
title="Title & Message"
|
||||
withTitle
|
||||
withMessage
|
||||
onSelection={this._updateSelectionText}
|
||||
showActionSheetWithOptions={showActionSheetWithOptions} />
|
||||
{this._renderSectionHeader('Android-Only Options')}
|
||||
<ShowActionSheetButton
|
||||
title="Icons"
|
||||
withIcons
|
||||
onSelection={this._updateSelectionText}
|
||||
showActionSheetWithOptions={showActionSheetWithOptions} />
|
||||
<ShowActionSheetButton
|
||||
title="Title, Message, & Icons"
|
||||
withTitle
|
||||
withMessage
|
||||
withIcons
|
||||
onSelection={this._updateSelectionText}
|
||||
showActionSheetWithOptions={showActionSheetWithOptions} />
|
||||
<ShowActionSheetButton
|
||||
title="Use Separators"
|
||||
withTitle
|
||||
withIcons
|
||||
withSeparators
|
||||
onSelection={this._updateSelectionText}
|
||||
showActionSheetWithOptions={showActionSheetWithOptions} />
|
||||
<ShowActionSheetButton
|
||||
title="Custom Styles"
|
||||
withTitle
|
||||
withMessage
|
||||
withIcons
|
||||
withCustomStyles
|
||||
onSelection={this._updateSelectionText}
|
||||
showActionSheetWithOptions={showActionSheetWithOptions} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SafeAreaView style={styles.flex}>
|
||||
<ScrollView style={styles.flex} contentContainerStyle={styles.contentContainer}>
|
||||
<Text style={styles.headerText}>
|
||||
{"Hello!\n\nThis is a simple example app to demonstrate @expo/react-native-action-sheet."}
|
||||
</Text>
|
||||
{this._renderButtons()}
|
||||
{this._renderSelectionText()}
|
||||
<Text style={styles.notes}>
|
||||
Note: Icons and custom text styles are only available on Android. Separators can only be toggled on Android; they always show on iOS.
|
||||
</Text>
|
||||
</ScrollView>
|
||||
</SafeAreaView>
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: {
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
contentContainer: {
|
||||
padding: 16,
|
||||
paddingVertical: 20,
|
||||
},
|
||||
headerText: {
|
||||
textAlign: 'center',
|
||||
fontSize: 16,
|
||||
marginBottom: 10,
|
||||
},
|
||||
notes: {
|
||||
marginTop: 32,
|
||||
},
|
||||
sectionHeaderText: {
|
||||
color: 'orange',
|
||||
textAlign: 'center',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 20,
|
||||
marginTop: 20,
|
||||
marginBottom: 10,
|
||||
},
|
||||
selectionText: {
|
||||
textAlign: 'center',
|
||||
color: 'blue',
|
||||
fontSize: 16,
|
||||
marginTop: 20,
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,90 @@
|
||||
import React from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MaterialIcons, Entypo } from '@expo/vector-icons';
|
||||
|
||||
const icon = (name) => <MaterialIcons key={name} name={name} size={24} />;
|
||||
|
||||
// A custom button that shows examples of different share sheet configurations
|
||||
class ShowActionSheetButton extends React.PureComponent {
|
||||
_showActionSheet = () => {
|
||||
const {
|
||||
withTitle,
|
||||
withMessage,
|
||||
withIcons,
|
||||
withSeparators,
|
||||
withCustomStyles,
|
||||
onSelection,
|
||||
showActionSheetWithOptions,
|
||||
} = this.props;
|
||||
// Same interface as https://facebook.github.io/react-native/docs/actionsheetios.html
|
||||
const options = ['Delete', 'Save', 'Share', 'Cancel'];
|
||||
const icons = withIcons ? [icon('delete'), icon('save'), icon('share'), icon('cancel')] : null;
|
||||
const title = withTitle ? 'Choose An Action' : null;
|
||||
const message = withMessage ? 'This library tries to mimic the native share sheets as close as possible.' : null;
|
||||
const destructiveButtonIndex = 0;
|
||||
const cancelButtonIndex = 3;
|
||||
const textStyle = withCustomStyles ? { fontSize: 20, fontWeight: '500', color: 'blue' } : null;
|
||||
const titleTextStyle = withCustomStyles ? { fontSize: 24, textAlign: 'center', fontWeight: '700', color: 'orange' } : null;
|
||||
const messageTextStyle = withCustomStyles ? { fontSize: 12, color: 'purple', textAlign: 'right' } : null;
|
||||
|
||||
showActionSheetWithOptions(
|
||||
{
|
||||
options,
|
||||
cancelButtonIndex,
|
||||
destructiveButtonIndex,
|
||||
title,
|
||||
message,
|
||||
icons, // Android only
|
||||
tintIcons: true, // Android only; default is true
|
||||
showSeparators: withSeparators, // Affects Android only; default is false
|
||||
textStyle, // Android only
|
||||
titleTextStyle, // Android only
|
||||
messageTextStyle, // Android only
|
||||
},
|
||||
buttonIndex => {
|
||||
// Do something here depending on the button index selected
|
||||
onSelection(buttonIndex);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { title } = this.props;
|
||||
return (
|
||||
<View style={{ margin: 6 }}>
|
||||
<Entypo.Button
|
||||
name="code"
|
||||
backgroundColor="#3e3e3e"
|
||||
onPress={this._showActionSheet}>
|
||||
<Text style={{
|
||||
fontSize: 15,
|
||||
color: '#fff'
|
||||
}}>{title}</Text>
|
||||
</Entypo.Button>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ShowActionSheetButton.propTypes = {
|
||||
title: PropTypes.string.isRequired,
|
||||
onSelection: PropTypes.func,
|
||||
showActionSheetWithOptions: PropTypes.func.isRequired,
|
||||
withTitle: PropTypes.bool,
|
||||
withMessage: PropTypes.bool,
|
||||
withIcons: PropTypes.bool,
|
||||
withSeparators: PropTypes.bool,
|
||||
withCustomStyles: PropTypes.bool,
|
||||
};
|
||||
|
||||
ShowActionSheetButton.defaultProps = {
|
||||
withTitle: false,
|
||||
withMessage: false,
|
||||
withIcons: false,
|
||||
withSeparators: false,
|
||||
withCustomStyles: false,
|
||||
onSelection: null,
|
||||
};
|
||||
|
||||
export default ShowActionSheetButton;
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@expo/react-native-action-sheet",
|
||||
"version": "1.1.0",
|
||||
"version": "2.0.1",
|
||||
"description": "A cross-platform ActionSheet for React Native",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
Reference in new issue
Block a user