Compare commits

...
48 Commits
Author SHA1 Message Date
Peter Piekarczyk 3b4439dd09 2.1.0 2019-01-30 19:01:32 -06:00
Evan Bacon 5ee7d9af74 Update ActionSheet.js 2019-01-30 16:04:02 -08:00
Brent Vatne 68ff6a1df9 Merge pull request #99 from expo/@evanbacon/expo-web/action-sheet
[Web] Added support for running with expo for web
2019-01-30 15:41:29 -08:00
Evan Bacon 3be83131c6 Updated docs 2019-01-30 15:19:52 -08:00
Evan Bacon 8e943863db Added Web 2019-01-30 15:16:21 -08:00
Peter Piekarczyk 040980edcf 2.0.1 2019-01-04 09:34:18 -06:00
Peter Piekarczyk 231630b875 2.0.1-1 2019-01-04 09:29:51 -06:00
Peter Piekarczyk 3c2ccec2d1 2.0.1-0 2019-01-04 09:29:45 -06:00
Peter Piekarczyk e35a4bffbb Merge pull request #96 from omerman/patch-1
Fixes an IOS issue for when not passing tintColor.
2019-01-04 09:27:25 -06:00
Omer c87602ea3b Remove tintColor default prop assignment. 2019-01-02 16:08:24 +02:00
Omer 8c1a319a3a Fixes an IOS issue for when not passing tintColor
Fixes "JSON value '<null>' of type NSNull cannot be converted to a UIColor. Did you forget to call processColor() on the JS side?"
2018-12-26 10:01:47 +02:00
Peter Piekarczyk ac17d305db Android UI & API Improvements #78 from jstheoriginal/master
Android UI & API Improvements
2018-12-11 16:43:22 -06:00
Justin Stanley 2b9d8e5ec6 Clarified tint ability of icons in the readme. 2018-11-11 13:37:42 -06:00
Justin Stanley fe8df1b685 Cleaned up App.js
Added SafeAreaView, added section headers to show Universal examples vs Android option examples, moved styles to static stylesheet.
2018-11-11 12:46:18 -06:00
Justin Stanley 74473dc4bd Fixed error with View.propTypes.style propType.
Changed it to PropTypes.object instead.
2018-11-11 12:45:21 -06:00
Justin Stanley 2ca8d6217e Removed unused NativeModules import. 2018-11-11 11:29:02 -06:00
Justin Stanley cebaef95a3 Updated the README formatting & wording for Options/Usage 2018-11-11 11:26:38 -06:00
Justin Stanley 15460b6a08 Added the new options/styles to ActionSheetProvider.
- it allows the user to apply it globally so they don’t need to be provided each time the method is called.
2018-11-11 11:25:59 -06:00
Justin Stanley afb0fefd7b Updated version to 2.0.0. 2018-11-11 10:02:58 -06:00
Justin Stanley 04a99ae94b Added missing semicolons & separatorStyle key should be optional. 2018-11-11 10:00:40 -06:00
Justin Stanley 432ae92569 Merge remote-tracking branch 'upstream/master' 2018-11-11 09:50:32 -06:00
Justin Stanley 2fa0e21086 Added separatorStyle prop so you can change the separator style if desired.
- Also defaulted title and message to null in _renderSheet.
2018-11-10 18:28:56 -06:00
Justin Stanley 33d77f4d04 Destructured options & more state items in _renderSheet 2018-11-10 18:09:14 -06:00
Justin Stanley 883399be33 defaultProps for ActionGroup due to user-supplied object
- instead, I’ve defaulted tintIcons (true) and textStyle ({}) at the render source
2018-11-10 18:03:41 -06:00
Justin Stanley 59e34c83b4 Use !! with && to render instead of ternary defaulting to null.
- Gave default empty object prop to textStyle
- Moved destructive colour to a constant
2018-11-10 17:13:55 -06:00
Peter Piekarczyk 2e75f9551d Merge pull request #83 from jsdario/patch-1
Allows Android textStyle to be declared at the Provider
2018-09-30 21:12:04 -05:00
Justin Stanley 42740e93a0 Improved ShowActionSheetButton helper class.
Moved buttons in example to a helper function since it’s long.
2018-08-21 14:41:35 -05:00
Justin Stanley 3c95b8a86d Added new Android-only prop: tintIcons.
- Defaults to true (same as current implementation).
- If set to false, the images will be rendered at their default colours (allows for full colour icons).
- Fixed incorrect typeof check for iconSource.
- Updated readme to reference new prop.
2018-08-21 14:40:04 -05:00
Justin Stanley f35de5dd25 Added icons to Android-only props in README. 2018-08-21 13:36:32 -05:00
Justin Stanley 8ef0ee36e7 Fixed crash on iOS if the icons array contains elements (like vector icons) rather than image source paths. 2018-08-21 13:36:11 -05:00
Justin Stanley 3a9bc50e84 You can now pass either react nodes or image sources in the icons array.
Also fixed the type for icons as it was an array of numbers previously.
2018-08-20 21:43:08 -05:00
Justin Stanley 7fe0aff0fe Use shorthand for null to set title and message to undefined.
Also undid change to formatting of return in render.
2018-08-20 19:38:06 -05:00
Justin Stanley 98528d9399 Use MaterialIcons for example icons rather than including images. 2018-08-20 19:34:44 -05:00
Justin Stanley 8ec19d3bfc Changed the show action sheet button functions into a component for clarity. 2018-08-20 19:24:13 -05:00
Justin Stanley 3e2a8d6c1d Removed flow type annotations from example app. 2018-08-20 18:58:10 -05:00
Justin Stanley 81f64772c7 Removed unused SafeAreaView. 2018-07-27 09:38:29 -05:00
Justin Stanley fa027c0f81 Added a few more Android only comments to example. 2018-07-27 01:44:47 -05:00
Justin Stanley f9376eb0ca Tweaked the example message so it works for iOS as well. 2018-07-27 01:39:15 -05:00
Justin Stanley 72d2e2a0ea Updated readme. 2018-07-27 01:25:41 -05:00
Justin Stanley acfd89c465 Added tintColor to Android to match iOS option. 2018-07-27 01:25:11 -05:00
Justin Stanley b2862ba08c Made Android Action Sheet match native Material sheet.
Added new Android props:
- title
- message
- titleTextStyle
- messageTextStyle
- showSeparators (defaults to false)

Made the Android sheet look as close to the specs at https://material.io/design/components/sheets-bottom.html#specs as possible.

Cleaned up the existing code.

Added examples for a bunch of different scenarios in the example app.

Fixed an existing crash where icons actually crashed due if you didn’t provide the textStyle prop.

Fixed a crash on iOS if you passed a null title or message.
2018-07-27 01:03:22 -05:00
Jesús Darío dfe724247c Allows Android textStyle to be declared at the Provider 2018-06-05 13:53:30 +02:00
Justin Stanley 150cf5fcca resolved merge conflict. 2018-03-05 21:56:31 -06:00
tim.farley b25206f7e6 define title styles when message is present in normal styles obj 2016-12-02 16:43:48 -07:00
Tim Farley ccfc3b448a Merge pull request #2 from dtdo-timfarley/android-support-for-message-prop
Adds support for message prop in Android
2016-12-02 16:11:16 -07:00
tim.farley 38150163c1 Adds support for message prop in Android 2016-12-02 16:09:38 -07:00
Tim Farley 88d27eba92 Merge pull request #1 from dtdo-timfarley/add-support-for-title-in-android
Adds Android support for an optional title prop.
2016-12-02 15:47:30 -07:00
tim.farley 9fee7dc244 Adds Android support for an optional title prop, similar to native iOS title 2016-12-02 15:39:29 -07:00
7 changed files with 482 additions and 132 deletions

No files matched your search

+9 -1
View File
@@ -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);
}
}
+187 -76
View File
@@ -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,
@@ -325,9 +421,10 @@ ActionSheet.defaultProps = {
useNativeDriver: true,
};
let TouchableComponent;
TouchableComponent = Platform.Version <= 20 ? TouchableOpacity : TouchableNativeFeedback;
let TouchableComponent = Platform.select({
web: TouchableOpacity,
default: Platform.Version <= 20 ? TouchableOpacity : TouchableNativeFeedback,
});
if (TouchableComponent !== TouchableNativeFeedback) {
TouchableComponent.SelectableBackground = () => ({});
@@ -353,19 +450,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 +469,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 +500,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',
},
});
+36 -3
View File
@@ -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);
},
};
}
+44 -16
View File
@@ -3,6 +3,7 @@
ActionSheet is a cross-platform React Native component that uses the native UIActionSheet on iOS and a JS implementation on Android. Almost a drop in replacement for [ActionSheetIOS](https://facebook.github.io/react-native/docs/actionsheetios.html) except it cannot be called statically.
## Installation
```
npm install @expo/react-native-action-sheet
```
@@ -12,7 +13,10 @@ npm install @expo/react-native-action-sheet
import ActionSheetProvider & connectActionSheet
```es6
import { ActionSheetProvider, connectActionSheet } from '@expo/react-native-action-sheet';
import {
ActionSheetProvider,
connectActionSheet,
} from '@expo/react-native-action-sheet';
```
wrap your top-level component with `<ActionSheetProvider />`
@@ -33,7 +37,9 @@ decorate the component you want to use the action sheet with `@connectActionShee
```es6
@connectActionSheet
class App extends React.Component { /* ... */ }
class App extends React.Component {
/* ... */
}
```
access actionSheet method as `this.props.showActionSheetWithOptions`
@@ -41,22 +47,44 @@ 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;
this.props.showActionSheetWithOptions({
options,
cancelButtonIndex,
destructiveButtonIndex,
},
(buttonIndex) => {
// Do something here depending on the button index selected
});
const options = ['Delete', 'Save', 'Cancel'];
const destructiveButtonIndex = 0;
const cancelButtonIndex = 2;
}
this.props.showActionSheetWithOptions(
{
options,
cancelButtonIndex,
destructiveButtonIndex,
},
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.
This library can also be used with on web with Expo.
### 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 +92,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
View File
@@ -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,
},
});
+90
View File
@@ -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;
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@expo/react-native-action-sheet",
"version": "1.1.0",
"version": "2.1.0",
"description": "A cross-platform ActionSheet for React Native",
"main": "index.js",
"scripts": {
@@ -15,7 +15,7 @@
"action-sheet"
],
"files": [
"ActionSheet.android.js",
"ActionSheet.js",
"ActionSheet.ios.js",
"ActionSheetProvider.js",
"connectActionSheet.js",