Compare commits

..
3 Commits
Author SHA1 Message Date
Peter Piekarczyk a34f3a95f3 1.1.2 2018-09-05 09:08:04 -05:00
Peter Piekarczyk 929cadc442 Merge branch 'master' of github.com:expo/react-native-action-sheet 2018-09-05 09:07:58 -05:00
Peter Piekarczyk 98b318c94d 1.1.1 2018-08-21 18:04:06 -05:00
42 changed files with 6143 additions and 9411 deletions

No files matched your search

-5
View File
@@ -1,5 +0,0 @@
node_modules
exmaple/node_modules
# generated by bob
lib/
-14
View File
@@ -1,14 +0,0 @@
module.exports = {
extends: [
'@react-native-community',
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
],
rules: {
// Turn off rules which are useless and annoying for this project
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-member-accessibility': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'react-native/no-inline-styles': 'off',
},
};
+99
View File
@@ -0,0 +1,99 @@
[ignore]
# We fork some components by platform.
.*/*.web.js
.*/node_modules/.*/*.android.js
# Some modules have their own node_modules with overlap
.*/node_modules/node-haste/.*
# Ugh
.*/node_modules/babel.*
.*/node_modules/babylon.*
.*/node_modules/invariant.*
# Ignore react and fbjs where there are overlaps, but don't ignore
# anything that react-native relies on
.*/node_modules/fbjs/lib/Map.js
.*/node_modules/fbjs/lib/ErrorUtils.js
# Flow has a built-in definition for the 'react' module which we prefer to use
# over the currently-untyped source
.*/node_modules/react/react.js
.*/node_modules/react/lib/React.js
.*/node_modules/react/lib/ReactDOM.js
.*/__mocks__/.*
.*/__tests__/.*
.*/commoner/test/source/widget/share.js
# Ignore commoner tests
.*/node_modules/commoner/test/.*
# See https://github.com/facebook/flow/issues/442
.*/react-tools/node_modules/commoner/lib/reader.js
# Ignore jest
.*/node_modules/jest-cli/.*
# Ignore Website
.*/website/.*
# Ignore generators
.*/local-cli/generator.*
# Ignore BUCK generated folders
.*\.buckd/
# Ignore RNPM
.*/local-cli/rnpm/.*
.*/node_modules/is-my-json-valid/test/.*\.json
.*/node_modules/iconv-lite/encodings/tables/.*\.json
.*/node_modules/y18n/test/.*\.json
.*/node_modules/spdx-license-ids/spdx-license-ids.json
.*/node_modules/spdx-exceptions/index.json
.*/node_modules/resolve/test/subdirs/node_modules/a/b/c/x.json
.*/node_modules/resolve/lib/core.json
.*/node_modules/jsonparse/samplejson/.*\.json
.*/node_modules/json5/test/.*\.json
.*/node_modules/ua-parser-js/test/.*\.json
.*/node_modules/builtin-modules/builtin-modules.json
.*/node_modules/binary-extensions/binary-extensions.json
.*/node_modules/url-regex/tlds.json
.*/node_modules/joi/.*\.json
.*/node_modules/isemail/.*\.json
.*/node_modules/tr46/.*\.json
[include]
[libs]
node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow
flow/
[options]
module.system=haste
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
experimental.strict_type_args=true
munge_underscores=true
module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-6]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-6]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
[version]
^0.26.0
-27
View File
@@ -1,27 +0,0 @@
name: Release
on:
push:
branches:
- master
jobs:
release:
name: Release
runs-on: ubuntu-18.04
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: 12
- name: Install dependencies
run: yarn
- name: Type check
run: npm run type-check
- name: Lint
run: npm run lint
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release
-4
View File
@@ -35,7 +35,6 @@ local.properties
#
node_modules/
npm-debug.log
yarn-error.log
# BUCK
buck-out/
@@ -44,6 +43,3 @@ android/app/libs
android/keystores/debug.keystore
coverage
# generated by bob
lib/
+27
View File
@@ -0,0 +1,27 @@
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
-5
View File
@@ -1,5 +0,0 @@
node_modules
example/node_modules
# generated by bob
lib/
-7
View File
@@ -1,7 +0,0 @@
{
"printWidth": 100,
"tabWidth": 2,
"singleQuote": true,
"jsxBracketSameLine": true,
"trailingComma": "es5"
}
-9
View File
@@ -1,9 +0,0 @@
{
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/github"
]
}
+408
View File
@@ -0,0 +1,408 @@
// @flow
import React from 'react';
import PropTypes from 'prop-types';
import {
Animated,
BackHandler,
Easing,
Platform,
StyleSheet,
Text,
Image,
NativeModules,
TouchableOpacity,
TouchableNativeFeedback,
TouchableWithoutFeedback,
View,
ScrollView,
} from 'react-native';
type ActionSheetOptions = {
options: Array<string>,
icons: ?Array<number>,
destructiveButtonIndex: ?number,
cancelButtonIndex: ?number,
textStyle: ?any,
};
type ActionGroupProps = {
options: Array<string>,
icons: ?Array<number>,
destructiveButtonIndex: ?number,
onSelect: (i: number) => boolean,
startIndex: number,
length: number,
textStyle: ?any,
};
type ActionSheetState = {
isVisible: boolean,
isAnimating: boolean,
options: ?ActionSheetOptions,
onSelect: ?(i: number) => void,
overlayOpacity: any,
sheetOpacity: any,
};
type ActionSheetProps = {
children: ?any,
useNativeDriver: ?boolean,
};
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)
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,
destructiveButtonIndex,
onSelect,
startIndex,
length,
textStyle,
} = this.props;
let optionViews = [];
let nativeFeedbackBackground = TouchableNativeFeedbackSafe.Ripple(
'rgba(180, 180, 180, 1)',
false
);
for (let i = startIndex; i < startIndex + length; i++) {
let color = '#212121';
if (i === destructiveButtonIndex) {
color = '#d32f2f';
}
let iconElement = undefined;
if (icons && icons[i]) {
const iconStyle = [styles.icon]
if (textStyle.color !== undefined && textStyle.color !== null) {
iconStyle.push({ tintColor: textStyle.color })
}
iconElement = <Image source={icons[i]} resizeMode="contain" style={iconStyle} />;
}
optionViews.push(
<TouchableNativeFeedbackSafe
key={i}
pressInDelay={0}
background={nativeFeedbackBackground}
onPress={() => onSelect(i)}
style={styles.button}>
{iconElement}
<Text style={[styles.text, { color }, textStyle]}>{options[i]}</Text>
</TouchableNativeFeedbackSafe>
);
if (i < startIndex + length - 1) {
optionViews.push(<View key={`separator-${i}`} style={styles.rowSeparator} />);
}
}
return (
<View style={styles.groupContainer}>
<ScrollView>{optionViews}</ScrollView>
</View>
);
}
}
// 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;
state: ActionSheetState = {
isVisible: false,
isAnimating: false,
options: null,
onSelect: null,
overlayOpacity: new Animated.Value(0),
sheetOpacity: new Animated.Value(0),
};
render() {
let { isVisible } = this.state;
let overlay = isVisible ? (
<Animated.View
style={[
styles.overlay,
{
opacity: this.state.overlayOpacity,
},
]}
/>
) : null;
let sheet = isVisible ? this._renderSheet() : null;
return (
<View style={{ flex: 1 }}>
{React.Children.only(this.props.children)}
{overlay}
{sheet}
</View>
);
}
_renderSheet() {
if (!this.state.options) {
return;
}
let numOptions = this.state.options.options.length;
return (
<TouchableWithoutFeedback onPress={this._selectCancelButton}>
<Animated.View
needsOffscreenAlphaCompositing={this.state.isAnimating}
style={[
styles.sheetContainer,
{
opacity: this.state.sheetOpacity,
transform: [
{
translateY: this.state.sheetOpacity.interpolate({
inputRange: [0, 1],
outputRange: [this._actionSheetHeight, 0],
}),
},
],
},
]}>
<View style={styles.sheet}
onLayout={(event) => { this._actionSheetHeight = event.nativeEvent.layout.height }}>
<ActionGroup
options={this.state.options.options}
icons={this.state.options.icons}
destructiveButtonIndex={this.state.options.destructiveButtonIndex}
onSelect={this._onSelect}
startIndex={0}
length={numOptions}
textStyle={this.state.options.textStyle}
/>
</View>
</Animated.View>
</TouchableWithoutFeedback>
);
}
showActionSheetWithOptions(
options: ActionSheetOptions,
onSelect: (i: number) => void,
onAnimateOut: () => void
) {
if (this.state.isVisible) {
return;
}
this.setState({
options,
onSelect,
isVisible: true,
isAnimating: true,
});
this.state.overlayOpacity.setValue(0);
this.state.sheetOpacity.setValue(0);
Animated.parallel([
Animated.timing(this.state.overlayOpacity, {
toValue: 0.2,
easing: EASING_OUT,
duration: OPACITY_ANIMATION_IN_TIME,
useNativeDriver: this.props.useNativeDriver,
}),
Animated.timing(this.state.sheetOpacity, {
toValue: 1,
easing: EASING_OUT,
duration: OPACITY_ANIMATION_IN_TIME,
useNativeDriver: this.props.useNativeDriver,
}),
]).start(result => {
if (result.finished) {
this.setState({
isAnimating: false,
});
}
});
this._animateOutCallback = onAnimateOut;
BackHandler.addEventListener(
'actionSheetHardwareBackPress',
this._selectCancelButton
);
}
_selectCancelButton = () => {
if (!this.state.options) {
return false;
}
if (typeof this.state.options.cancelButtonIndex === 'number') {
return this._onSelect(this.state.options.cancelButtonIndex);
} else {
return this._animateOut();
}
};
_onSelect = (index: number): boolean => {
if (this.state.isAnimating) {
return false;
}
this.state.onSelect && this.state.onSelect(index);
return this._animateOut();
};
_animateOut = (): boolean => {
if (this.state.isAnimating) {
return false;
}
BackHandler.removeEventListener(
'actionSheetHardwareBackPress',
this._selectCancelButton
);
this.setState({
isAnimating: true,
});
Animated.parallel([
Animated.timing(this.state.overlayOpacity, {
toValue: 0,
easing: EASING_IN,
duration: OPACITY_ANIMATION_OUT_TIME,
useNativeDriver: this.props.useNativeDriver,
}),
Animated.timing(this.state.sheetOpacity, {
toValue: 0,
easing: EASING_IN,
duration: OPACITY_ANIMATION_OUT_TIME,
useNativeDriver: this.props.useNativeDriver,
}),
]).start(result => {
if (result.finished) {
this.setState({
isVisible: false,
isAnimating: false,
});
if (typeof this._animateOutCallback === 'function') {
this._animateOutCallback();
this._animateOutCallback = null;
}
}
});
return true;
};
}
ActionSheet.defaultProps = {
useNativeDriver: true,
};
let TouchableComponent;
TouchableComponent = Platform.Version <= 20 ? TouchableOpacity : TouchableNativeFeedback;
if (TouchableComponent !== TouchableNativeFeedback) {
TouchableComponent.SelectableBackground = () => ({});
TouchableComponent.SelectableBackgroundBorderless = () => ({});
TouchableComponent.Ripple = (color, borderless) => ({});
}
class TouchableNativeFeedbackSafe extends React.Component {
static SelectableBackground = TouchableComponent.SelectableBackground;
static SelectableBackgroundBorderless = TouchableComponent.SelectableBackgroundBorderless;
static Ripple = TouchableComponent.Ripple;
render() {
if (TouchableComponent === TouchableNativeFeedback) {
return (
<TouchableComponent {...this.props} style={{}}>
<View style={this.props.style}>{this.props.children}</View>
</TouchableComponent>
);
} else {
return <TouchableComponent {...this.props}>{this.props.children}</TouchableComponent>;
}
}
}
let styles = StyleSheet.create({
groupContainer: {
backgroundColor: '#fefefe',
borderColor: '#ffffff',
borderTopWidth: StyleSheet.hairlineWidth,
overflow: 'hidden',
paddingVertical: 8,
},
button: {
justifyContent: 'flex-start',
alignItems: 'center',
flexDirection: 'row',
height: 48,
paddingHorizontal: 16,
},
icon: {
width: 24,
height: 24,
marginRight: 32,
},
text: {
fontSize: 16,
textAlignVertical: 'center',
},
rowSeparator: {
backgroundColor: '#dddddd',
height: 1,
flex: 1,
},
overlay: {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
backgroundColor: 'black',
},
sheetContainer: {
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
top: 0,
backgroundColor: 'transparent',
alignItems: 'flex-end',
justifyContent: 'center',
flexDirection: 'row',
},
sheet: {
flex: 1,
backgroundColor: 'transparent',
},
});
+14
View File
@@ -0,0 +1,14 @@
'use strict';
import React from 'react';
import { ActionSheetIOS, View } from 'react-native';
export default class ActionSheet extends React.Component {
render() {
return <View style={{ flex: 1 }}>{React.Children.only(this.props.children)}</View>;
}
showActionSheetWithOptions(options, onSelect) {
ActionSheetIOS.showActionSheetWithOptions(options, onSelect);
}
}
+28
View File
@@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import ActionSheet from './ActionSheet';
export default class ActionSheetProvider extends React.Component {
static propTypes = {
children: PropTypes.any,
};
static childContextTypes = {
showActionSheetWithOptions: PropTypes.func,
};
getChildContext() {
return {
showActionSheetWithOptions: (...args) =>
this._actionSheetRef.showActionSheetWithOptions(...args),
};
}
render() {
return (
<ActionSheet ref={component => (this._actionSheetRef = component)}>
{React.Children.only(this.props.children)}
</ActionSheet>
);
}
}
-1
View File
@@ -1 +0,0 @@
# Changelog
+32 -147
View File
@@ -1,182 +1,67 @@
# react-native-action-sheet
[![npm](https://img.shields.io/npm/v/@expo/react-native-action-sheet.svg?style=flat-square)](https://www.npmjs.com/package/@expo/react-native-action-sheet)
[![License: MIT](https://img.shields.io/github/license/nd-02110114/goofi-mobile.svg)](https://opensource.org/licenses/MIT)
[![Slack](https://slack.expo.io/badge.svg)](https://slack.expo.io)
# react-native-action-sheet [![Slack](https://slack.expo.io/badge.svg)](https://slack.expo.io)
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.
| iOS | Android | Web |
|---------------------------|---------------------------|---------------------------|
| <img src="https://raw.githubusercontent.com/expo/react-native-action-sheet/master/gif/ios.gif" width="200" height="400"/> | <img src="https://raw.githubusercontent.com/expo/react-native-action-sheet/master/gif/android.gif" width="200" height="400"/> | <img src="https://raw.githubusercontent.com/expo/react-native-action-sheet/master/gif/web.gif" width="400" height="400"/> |
## Installation
```
$ npm install @expo/react-native-action-sheet -S
```
or
```
$ yarn add @expo/react-native-action-sheet
npm install @expo/react-native-action-sheet
```
## A basic ActionSheet Setup
### 1. Connect your component which uses showActionSheetWithOptions.
import ActionSheetProvider & connectActionSheet
```es6
import { connectActionSheet } from '@expo/react-native-action-sheet'
class App extends React.Component {
/* ... */
}
const ConnectedApp = connectActionSheet(App)
export default ConnectedApp
import { ActionSheetProvider, connectActionSheet } from '@expo/react-native-action-sheet';
```
`App` component can access the actionSheet method as `this.props.showActionSheetWithOptions`
wrap your top-level component with `<ActionSheetProvider />`
```es6
_onOpenActionSheet = () => {
// Same interface as https://facebook.github.io/react-native/docs/actionsheetios.html
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
},
);
};
```
You can use a hook instead of the higher order component if you are on React 16.8 or newer.
```es6
import { useActionSheet } from '@expo/react-native-action-sheet'
export default function App () {
const { showActionSheetWithOptions } = useActionSheet();
/* ... */
}
```
### 2. Wrap your top-level component with `<ActionSheetProvider />`
```es6
import { ActionSheetProvider } from '@expo/react-native-action-sheet'
class AppContainer extends React.Component {
render() {
return (
<ActionSheetProvider>
<ConnectedApp />
<App />
</ActionSheetProvider>
);
}
}
```
## Options
decorate the component you want to use the action sheet with `@connectActionSheet`
The goal of this library is to mimic the native iOS and Android ActionSheets as closely as possible.
```es6
@connectActionSheet
class App extends React.Component { /* ... */ }
```
This library can also be used with on web with Expo.
access actionSheet method as `this.props.showActionSheetWithOptions`
### Universal Props
```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
});
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/Web-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.
| Name | Type | Required | Default |
| -----------------| ----------------------------------| -------- | ------- |
| icons | array of required images or icons | No | |
| tintIcons | boolean | No | true |
| textStyle | TextStyle | No | |
| titleTextStyle | TextStyle | No | |
| messageTextStyle | TextStyle | No | |
| showSeparators | boolean | No | false |
| separatorStyle | ViewStyle | No | |
#### `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` (optional)
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` (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` (optional)
Apply any text style props to the title if present.
#### `messageTextStyle` (optional)
Apply any text style props to the message if present.
#### `showSeparators`: (optional)
Show separators between items. On iOS, separators always show so this prop has no effect.
#### `separatorStyle`: (optional)
Modify the look of the separators rather than use the default look.
## Try it out
Try it in Expo: https://expo.io/@community/react-native-action-sheet-example
## Example
## Usage
See the [example app](https://github.com/expo/react-native-action-sheet/tree/master/example).
### Usage
```
$ cd example
$ yarn
// build simulator
$ yarn ios
$ yarn android
// web
$ yarn web
```
## Development
### Setup
```
$ git clone [email protected]:expo/react-native-action-sheet.git
$ cd react-native-action-sheet
$ yarn
```
### Build
We use [bob](https://github.com/react-native-community/bob).
```
$ yarn build
```
### Lint & Format
```
// tsc
$ yarn type-check
// ESLint
$ yarn lint
// prettier
$ yarn fmt
```
[See the example app source](https://github.com/expo/react-native-action-sheet/tree/master/example)
+20
View File
@@ -0,0 +1,20 @@
import hoistStatics from 'hoist-non-react-statics';
import React from 'react';
import PropTypes from 'prop-types';
export default function connectActionSheet(WrappedComponent) {
const ConnectedActionSheet = (props, context) => {
return (
<WrappedComponent
{...props}
showActionSheetWithOptions={context.showActionSheetWithOptions}
/>
);
};
ConnectedActionSheet.contextTypes = {
showActionSheetWithOptions: PropTypes.func,
};
return hoistStatics(ConnectedActionSheet, WrappedComponent);
}
+8
View File
@@ -0,0 +1,8 @@
{
"presets": ["babel-preset-expo"],
"env": {
"development": {
"plugins": ["transform-react-jsx-source"]
}
}
}
+60
View File
@@ -0,0 +1,60 @@
import Expo from 'expo';
import React from 'react';
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
import { Entypo } from '@expo/vector-icons';
import { ActionSheetProvider, connectActionSheet } from '@expo/react-native-action-sheet';
export default class AppContainer extends React.Component {
render() {
return (
<ActionSheetProvider>
<App />
</ActionSheetProvider>
);
}
}
@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>
<Entypo.Button name="code" backgroundColor="#3e3e3e" onPress={this._onOpenActionSheet}>
<Text style={{ fontSize: 15, color: '#fff' }}>Open action sheet</Text>
</Entypo.Button>
</View>
);
}
_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 styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
-180
View File
@@ -1,180 +0,0 @@
import * as React from 'react';
import { StyleSheet, Text, View, ScrollView, SafeAreaView } from 'react-native';
import {
ActionSheetProvider,
connectActionSheet,
ActionSheetOptions,
ActionSheetProps,
} from '@expo/react-native-action-sheet';
import ShowActionSheetButton from './ShowActionSheetButton';
type Props = ActionSheetProps;
interface State {
selectedIndex: number | null;
}
class App extends React.Component<Props, State> {
state: State = {
selectedIndex: null,
};
_updateSelectionText = (selectedIndex: number) => {
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>;
};
_renderSectionHeader = (text: string) => {
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}
/>
<ShowActionSheetButton
title="Nested Action Sheets"
onSelection={index => {
if (index < 3) {
showActionSheetWithOptions(
{
title: 'Sub Action Sheet',
options: ['One', 'Two', 'Three', 'Done'],
cancelButtonIndex: 3,
},
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 ConnectedApp = connectActionSheet<{}>(App);
export default class AppContainer extends React.Component {
render() {
return (
<ActionSheetProvider>
<ConnectedApp />
</ActionSheetProvider>
);
}
}
const styles = StyleSheet.create({
flex: {
flex: 1,
},
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,
},
});
-122
View File
@@ -1,122 +0,0 @@
import * as React from 'react';
import { Text, View, TextStyle } from 'react-native';
import { MaterialIcons, Entypo } from '@expo/vector-icons';
import { ActionSheetOptions } from '@expo/react-native-action-sheet';
const icon = (name: string) => <MaterialIcons key={name} name={name} size={24} />;
interface Props {
title: string;
showActionSheetWithOptions: (
optons: ActionSheetOptions,
callback: (buttonIndex: number) => void
) => void;
onSelection: (index: number) => void;
withTitle?: boolean;
withMessage?: boolean;
withIcons?: boolean;
withSeparators?: boolean;
withCustomStyles?: boolean;
}
// A custom button that shows examples of different share sheet configurations
export default class ShowActionSheetButton extends React.PureComponent<Props> {
static defaultProps = {
withTitle: false,
withMessage: false,
withIcons: false,
withSeparators: false,
withCustomStyles: false,
onSelection: null,
};
_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')]
: undefined;
const title = withTitle ? 'Choose An Action' : undefined;
const message = withMessage
? 'This library tries to mimic the native share sheets as close as possible.'
: undefined;
const destructiveButtonIndex = 0;
const cancelButtonIndex = 3;
const textStyle: TextStyle | undefined = withCustomStyles
? {
fontSize: 20,
fontWeight: '500',
color: 'blue',
}
: undefined;
const titleTextStyle: TextStyle | undefined = withCustomStyles
? {
fontSize: 24,
textAlign: 'center',
fontWeight: '700',
color: 'orange',
}
: undefined;
const messageTextStyle: TextStyle | undefined = withCustomStyles
? {
fontSize: 12,
color: 'purple',
textAlign: 'right',
}
: undefined;
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>
);
}
}
+6 -8
View File
@@ -4,15 +4,13 @@
"description": "A cross-platform ActionSheet component. https://github.com/expo/react-native-action-sheet",
"slug": "react-native-action-sheet-example",
"privacy": "public",
"sdkVersion": "35.0.0",
"version": "3.0.0",
"sdkVersion": "25.0.0",
"version": "1.0.0",
"orientation": "portrait",
"primaryColor": "#cccccc",
"icon": "https://s3.amazonaws.com/exp-brand-assets/ExponentEmptyManifest_192.png",
"platforms": [
"android",
"ios",
"web"
]
"packagerOpts": {
"assetExts": ["ttf", "mp4"]
}
}
}
}
-6
View File
@@ -1,6 +0,0 @@
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};
+7 -23
View File
@@ -1,30 +1,14 @@
{
"name": "example",
"version": "3.0.0",
"description": "Hello react native action sheet!!",
"version": "0.0.0",
"description": "Hello Expo!",
"author": "[email protected]",
"private": true,
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"ios": "expo start --ios",
"android": "expo start --android",
"web": "expo start --web",
"type-check": "tsc --noEmit"
},
"dependencies": {
"@expo/react-native-action-sheet": "../src",
"@expo/vector-icons": "^10.0.0",
"expo": "^35.0.0",
"hoist-non-react-statics": "^3.3.0",
"react": "16.8.3",
"react-dom": "16.8.3",
"react-native": "https://github.com/expo/react-native/archive/sdk-35.0.0.tar.gz",
"react-native-web": "^0.11.5"
},
"devDependencies": {
"@types/react": "^16.8.23",
"@types/react-native": "^0.57.65",
"babel-preset-expo": "^7.0.0",
"typescript": "^3.5.3"
"@expo/react-native-action-sheet": "../",
"expo": "^25.0.0",
"react": "16.2.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-25.0.0.tar.gz"
}
}
-15
View File
@@ -1,15 +0,0 @@
{
"compilerOptions": {
"esModuleInterop": true,
"jsx": "react-native",
"lib": ["esnext"],
"moduleResolution": "node",
"noEmit": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": true,
"target": "esnext",
"typeRoots": ["./node_modules/@types"],
},
"include": ["./**.tsx"],
}
+2257 -3625
View File
File diff suppressed because it is too large. Load diff
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 MiB

Vendored
+14
View File
@@ -0,0 +1,14 @@
import { ActionSheetIOSOptions } from 'react-native';
export interface ActionSheetProps {
showActionSheetWithOptions: (
options: ActionSheetIOSOptions,
callback: (buttonIndex: number) => void,
) => void;
}
export function connectActionSheet<T>(
WrappedComponent: React.ComponentType<T & ActionSheetProps>,
): React.ComponentClass<T>;
export const ActionSheetProvider: React.ComponentClass;
+4
View File
@@ -0,0 +1,4 @@
module.exports = require('./ActionSheet');
module.exports.ActionSheetProvider = require('./ActionSheetProvider').default;
module.exports.connectActionSheet = require('./connectActionSheet').default;
+21 -60
View File
@@ -1,82 +1,43 @@
{
"name": "@expo/react-native-action-sheet",
"version": "3.3.0",
"version": "1.1.2",
"description": "A cross-platform ActionSheet for React Native",
"react-native": "lib/module/index.js",
"types": "lib/typescript/index.d.ts",
"main": "lib/commonjs/index.js",
"module": "lib/module/index.js",
"files": [
"lib"
],
"main": "index.js",
"scripts": {
"prepare": "bob build",
"build": "bob build",
"type-check": "tsc --noEmit",
"lint": "eslint . --ext .ts,.tsx --fix",
"fmt": "prettier --write \"{src,example}/**/*.{ts,tsx}\"",
"semantic-release": "semantic-release"
"flow": "flow; test $? -eq 0 -o $? -eq 2"
},
"repository": {
"type": "git",
"url": "https://github.com/expo/react-native-action-sheet.git"
},
"keywords": [
"expo",
"react-native",
"action-sheet"
],
"files": [
"ActionSheet.android.js",
"ActionSheet.ios.js",
"ActionSheetProvider.js",
"connectActionSheet.js",
"index.js",
"index.d.ts",
"README.md"
],
"author": "Jesse Ruder <[email protected]>",
"license": "MIT",
"bugs": {
"url": "https://github.com/expo/react-native-action-sheet/issues"
},
"homepage": "https://github.com/expo/react-native-action-sheet",
"devDependencies": {
"@react-native-community/bob": "^0.6.1",
"@react-native-community/eslint-config": "^0.0.5",
"@semantic-release/changelog": "^3.0.5",
"@types/react": "^16.8.23",
"@types/react-native": "^0.57.65",
"@typescript-eslint/eslint-plugin": "^1.12.0",
"@typescript-eslint/parser": "^1.12.0",
"eslint": "^6.0.1",
"eslint-config-prettier": "^6.0.0",
"eslint-plugin-prettier": "3.1.0",
"husky": "^3.0.0",
"lint-staged": "^9.2.0",
"prettier": "^1.18.2",
"react": "^16.8.6",
"react-native": "https://github.com/expo/react-native/archive/sdk-33.0.0.tar.gz",
"semantic-release": "^15.13.28",
"typescript": "^3.5.3"
},
"peerDependencies": {
"react": ">=16.3.0"
},
"@react-native-community/bob": {
"source": "src",
"output": "lib",
"targets": [
"commonjs",
"module",
"typescript"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,ts,tsx}": [
"yarn lint",
"yarn fmt",
"git add"
]
},
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.1",
"hoist-non-react-statics": "^3.3.0"
"hoist-non-react-statics": "^2.2.2",
"prop-types": "^15.5.10"
},
"devDependencies": {
"@types/react": "^16.4.6",
"@types/react-native": "^0.56.0",
"flow-bin": "^0.26.0",
"react": "^15.6.1",
"react-native": "^0.28.0"
}
}
-162
View File
@@ -1,162 +0,0 @@
import * as React from 'react';
import { StyleSheet, Text, Image, View, ScrollView } from 'react-native';
import TouchableNativeFeedbackSafe from './TouchableNativeFeedbackSafe';
import { ActionSheetOptions } from '../types';
type Props = ActionSheetOptions & {
tintIcons: boolean | null;
onSelect: (i: number) => boolean;
startIndex: number;
length: number;
};
const BLACK_54PC_TRANSPARENT = '#0000008a';
const BLACK_87PC_TRANSPARENT = '#000000de';
const DESTRUCTIVE_COLOR = '#d32f2f';
export default class ActionGroup extends React.Component<Props> {
static defaultProps = {
title: null,
message: null,
showSeparators: false,
tintIcons: true,
textStyle: {},
};
render() {
return (
<View style={styles.groupContainer}>
{this._renderTitleContent()}
<ScrollView>{this._renderOptionViews()}</ScrollView>
</View>
);
}
_renderRowSeparator = (key: string | number) => {
return (
<View key={`separator-${key}`} 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>
);
};
_renderIconElement = (iconSource: React.ReactNode | null, color: string) => {
const { tintIcons } = this.props;
if (!iconSource) {
return null;
}
if (typeof iconSource === 'number') {
const iconStyle = [styles.icon, { tintColor: tintIcons ? color : undefined }];
return <Image fadeDuration={0} source={iconSource} resizeMode="contain" style={iconStyle} />;
} else {
return <View style={styles.icon}>{iconSource}</View>;
}
};
_renderOptionViews = () => {
const {
options,
icons,
destructiveButtonIndex,
onSelect,
startIndex,
length,
textStyle,
tintColor,
showSeparators,
} = this.props;
const optionViews: React.ReactNode[] = [];
const nativeFeedbackBackground = TouchableNativeFeedbackSafe.Ripple(
'rgba(180, 180, 180, 1)',
false
);
for (let i = startIndex; i < startIndex + length; i++) {
const defaultColor = tintColor
? tintColor
: (textStyle || {}).color || BLACK_87PC_TRANSPARENT;
const color = i === destructiveButtonIndex ? DESTRUCTIVE_COLOR : defaultColor;
const iconSource = icons != null ? icons[i] : null;
optionViews.push(
<TouchableNativeFeedbackSafe
key={i}
pressInDelay={0}
background={nativeFeedbackBackground}
onPress={() => onSelect(i)}
style={styles.button}>
{this._renderIconElement(iconSource, color)}
<Text style={[styles.text, textStyle, { color }]}>{options[i]}</Text>
</TouchableNativeFeedbackSafe>
);
if (showSeparators && i < startIndex + length - 1) {
optionViews.push(this._renderRowSeparator(i));
}
}
return optionViews;
};
}
const styles = StyleSheet.create({
button: {
justifyContent: 'flex-start',
alignItems: 'center',
flexDirection: 'row',
height: 56,
paddingHorizontal: 16,
},
groupContainer: {
backgroundColor: '#ffffff',
overflow: 'hidden',
},
icon: {
width: 24,
height: 24,
marginRight: 32,
},
message: {
marginTop: 12,
fontSize: 14,
color: BLACK_54PC_TRANSPARENT,
textAlignVertical: 'center',
},
rowSeparator: {
backgroundColor: '#dddddd',
height: 1,
width: '100%',
},
text: {
fontSize: 16,
color: BLACK_87PC_TRANSPARENT,
textAlignVertical: 'center',
},
title: {
fontSize: 16,
color: BLACK_54PC_TRANSPARENT,
textAlignVertical: 'center',
},
titleContainer: {
alignItems: 'flex-start',
padding: 16,
paddingTop: 24,
},
});
@@ -1,44 +0,0 @@
import * as React from 'react';
import {
Platform,
TouchableNativeFeedback,
TouchableOpacity,
View,
TouchableWithoutFeedbackProps,
} from 'react-native';
// This TouchableOpacity has the same staic method of TouchableNativeFeedback
class CustomTouchableOpacity extends TouchableOpacity {
static SelectableBackground = () => ({});
static SelectableBackgroundBorderless = () => ({});
static Ripple = (color: string, borderless?: boolean) => ({});
}
const TouchableComponent = Platform.select({
web: CustomTouchableOpacity,
default: Platform.Version <= 20 ? CustomTouchableOpacity : TouchableNativeFeedback,
});
type Props = TouchableWithoutFeedbackProps & {
pressInDelay: number;
background: any;
};
export default class TouchableNativeFeedbackSafe extends React.Component<Props> {
static SelectableBackground = TouchableComponent.SelectableBackground;
static SelectableBackgroundBorderless = TouchableComponent.SelectableBackgroundBorderless;
static Ripple = TouchableComponent.Ripple;
render() {
if (TouchableComponent === TouchableNativeFeedback) {
return (
<TouchableComponent {...this.props} style={{}}>
<View style={this.props.style}>{this.props.children}</View>
</TouchableComponent>
);
}
// @ts-ignore: JSX element type 'TouchableComponent' does not have any construct or call signatures
return <TouchableComponent {...this.props}>{this.props.children}</TouchableComponent>;
}
}
-25
View File
@@ -1,25 +0,0 @@
import * as React from 'react';
import { ActionSheetIOS, View } from 'react-native';
import { ActionSheetIOSOptions } from '../types';
interface Props {
readonly children: React.ReactNode;
}
type onSelect = (buttonIndex: number) => void;
export default class ActionSheet extends React.Component<Props> {
render() {
return <View style={{ flex: 1 }}>{React.Children.only(this.props.children)}</View>;
}
showActionSheetWithOptions(options: ActionSheetIOSOptions, onSelect: onSelect) {
const iosOptions: ActionSheetIOSOptions = {
...options,
// A null title or message on iOS causes a crash
title: options.title || undefined,
message: options.message || undefined,
};
ActionSheetIOS.showActionSheetWithOptions(iosOptions, onSelect);
}
}
-273
View File
@@ -1,273 +0,0 @@
import * as React from 'react';
import {
Animated,
BackHandler,
Easing,
StyleSheet,
TouchableWithoutFeedback,
View,
} from 'react-native';
import ActionGroup from './ActionGroup';
import { ActionSheetOptions } from '../types';
interface State {
isVisible: boolean;
isAnimating: boolean;
options: ActionSheetOptions | null;
onSelect: ((i: number) => void) | null;
overlayOpacity: any;
sheetOpacity: any;
}
interface Props {
readonly useNativeDriver: boolean | undefined;
}
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);
// Has same API as https://facebook.github.io/react-native/docs/actionsheetios.html
export default class ActionSheet extends React.Component<Props, State> {
static defaultProps = {
useNativeDriver: true,
};
_actionSheetHeight = 360;
state: State = {
isVisible: false,
isAnimating: false,
options: null,
onSelect: null,
overlayOpacity: new Animated.Value(0),
sheetOpacity: new Animated.Value(0),
};
_deferNextShow?: () => void = undefined;
_setActionSheetHeight = ({ nativeEvent }: any) =>
(this._actionSheetHeight = nativeEvent.layout.height);
render() {
const { isVisible, overlayOpacity } = this.state;
const overlay = isVisible ? (
<Animated.View
style={[
styles.overlay,
{
opacity: overlayOpacity,
},
]}
/>
) : null;
return (
<View
style={{
flex: 1,
}}>
{React.Children.only(this.props.children)}
{overlay}
{isVisible ? this._renderSheet() : null}
</View>
);
}
_renderSheet() {
const { options, isAnimating, sheetOpacity } = this.state;
if (!options) {
return null;
}
const {
options: optionsArray,
icons,
tintIcons,
destructiveButtonIndex,
textStyle,
tintColor,
title,
titleTextStyle,
message,
messageTextStyle,
showSeparators,
separatorStyle,
} = options;
return (
<TouchableWithoutFeedback onPress={this._selectCancelButton}>
<Animated.View
needsOffscreenAlphaCompositing={isAnimating}
style={[
styles.sheetContainer,
{
opacity: sheetOpacity,
transform: [
{
translateY: sheetOpacity.interpolate({
inputRange: [0, 1],
outputRange: [this._actionSheetHeight, 0],
}),
},
],
},
]}>
<View style={styles.sheet} onLayout={this._setActionSheetHeight}>
<ActionGroup
options={optionsArray}
icons={icons}
tintIcons={tintIcons === undefined ? true : tintIcons}
destructiveButtonIndex={destructiveButtonIndex}
onSelect={this._onSelect}
startIndex={0}
length={optionsArray.length}
textStyle={textStyle || {}}
tintColor={tintColor}
title={title || undefined}
titleTextStyle={titleTextStyle}
message={message || undefined}
messageTextStyle={messageTextStyle}
showSeparators={showSeparators}
separatorStyle={separatorStyle}
/>
</View>
</Animated.View>
</TouchableWithoutFeedback>
);
}
showActionSheetWithOptions = (options: ActionSheetOptions, onSelect: (i: number) => void) => {
const { isVisible, isAnimating, overlayOpacity, sheetOpacity } = this.state;
if (isVisible) {
this._deferNextShow = this.showActionSheetWithOptions.bind(this, options, onSelect);
return;
}
this.setState({
options,
onSelect,
isVisible: true,
isAnimating: true,
});
overlayOpacity.setValue(0);
sheetOpacity.setValue(0);
Animated.parallel([
Animated.timing(overlayOpacity, {
toValue: 0.32,
easing: EASING_OUT,
duration: OPACITY_ANIMATION_IN_TIME,
useNativeDriver: this.props.useNativeDriver,
}),
Animated.timing(sheetOpacity, {
toValue: 1,
easing: EASING_OUT,
duration: OPACITY_ANIMATION_IN_TIME,
useNativeDriver: this.props.useNativeDriver,
}),
]).start(result => {
if (result.finished) {
this.setState({
isAnimating: false,
});
this._deferNextShow = undefined;
}
});
// @ts-ignore: Argument of type '"actionSheetHardwareBackPress"' is not assignable to parameter of type '"hardwareBackPress"'
BackHandler.addEventListener('actionSheetHardwareBackPress', this._selectCancelButton);
};
_selectCancelButton = () => {
const { options } = this.state;
if (!options) {
return false;
}
if (typeof options.cancelButtonIndex === 'undefined') {
return;
} else if (typeof options.cancelButtonIndex === 'number') {
return this._onSelect(options.cancelButtonIndex);
} else {
return this._animateOut();
}
};
_onSelect = (index: number): boolean => {
const { isAnimating, onSelect } = this.state;
if (isAnimating) {
return false;
}
onSelect && onSelect(index);
return this._animateOut();
};
_animateOut = (): boolean => {
const { isAnimating, overlayOpacity, sheetOpacity } = this.state;
if (isAnimating) {
return false;
}
// @ts-ignore: Argument of type '"actionSheetHardwareBackPress"' is not assignable to parameter of type '"hardwareBackPress"'
BackHandler.removeEventListener('actionSheetHardwareBackPress', this._selectCancelButton);
this.setState({
isAnimating: true,
});
Animated.parallel([
Animated.timing(overlayOpacity, {
toValue: 0,
easing: EASING_IN,
duration: OPACITY_ANIMATION_OUT_TIME,
useNativeDriver: this.props.useNativeDriver,
}),
Animated.timing(sheetOpacity, {
toValue: 0,
easing: EASING_IN,
duration: OPACITY_ANIMATION_OUT_TIME,
useNativeDriver: this.props.useNativeDriver,
}),
]).start(result => {
if (result.finished) {
this.setState({
isVisible: false,
isAnimating: false,
});
if (this._deferNextShow) {
this._deferNextShow();
}
}
});
return true;
};
}
const styles = StyleSheet.create({
overlay: {
position: 'absolute',
top: 0,
right: 0,
bottom: 0,
left: 0,
backgroundColor: 'black',
},
sheetContainer: {
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
top: 0,
backgroundColor: 'transparent',
alignItems: 'flex-end',
justifyContent: 'center',
flexDirection: 'row',
},
sheet: {
flex: 1,
backgroundColor: 'transparent',
},
});
-36
View File
@@ -1,36 +0,0 @@
import * as React from 'react';
import ActionSheet from './ActionSheet';
import { Provider } from './context';
import { ActionSheetOptions } from './types';
interface Props {
children: React.ReactNode;
}
export default class ActionSheetProvider extends React.Component<Props> {
_actionSheetRef: React.RefObject<ActionSheet>;
constructor(props: Props) {
super(props);
this._actionSheetRef = React.createRef();
}
getContext = () => {
return {
showActionSheetWithOptions: (options: ActionSheetOptions, callback: (i: number) => void) => {
this._actionSheetRef.current !== null &&
this._actionSheetRef.current.showActionSheetWithOptions(options, callback);
},
};
};
render() {
return (
<Provider value={this.getContext()}>
<ActionSheet ref={this._actionSheetRef}>
{React.Children.only(this.props.children)}
</ActionSheet>
</Provider>
);
}
}
-22
View File
@@ -1,22 +0,0 @@
import * as React from 'react';
import { Consumer } from './context';
import hoistNonReactStatic from 'hoist-non-react-statics';
import { ActionSheetProps } from './types';
export default function connectActionSheet<OwnProps = any>(
WrappedComponent: React.ComponentType<OwnProps & ActionSheetProps>
) {
const ConnectedActionSheet = (props: OwnProps) => {
return (
<Consumer>
{({ showActionSheetWithOptions }) => {
return (
<WrappedComponent {...props} showActionSheetWithOptions={showActionSheetWithOptions} />
);
}}
</Consumer>
);
};
return hoistNonReactStatic(ConnectedActionSheet, WrappedComponent);
}
-14
View File
@@ -1,14 +0,0 @@
import * as React from 'react';
import { ActionSheetOptions, ActionSheetProps } from './types';
const context = React.createContext<ActionSheetProps>({
showActionSheetWithOptions: (options: ActionSheetOptions, callback: (i: number) => void) => {},
});
export function useActionSheet() {
return React.useContext(context);
}
const { Provider, Consumer } = context;
export { Provider, Consumer };
-4
View File
@@ -1,4 +0,0 @@
export { default as ActionSheetProvider } from './ActionSheetProvider';
export { default as connectActionSheet } from './connectActionSheet';
export { useActionSheet } from './context';
export * from './types';
-27
View File
@@ -1,27 +0,0 @@
import * as React from 'react';
import { TextStyle, ViewStyle } from 'react-native';
export interface ActionSheetProps {
showActionSheetWithOptions: (options: ActionSheetOptions, callback: (i: number) => void) => void;
}
// for iOS
export interface ActionSheetIOSOptions {
options: string[];
title?: string;
message?: string;
tintColor?: string;
cancelButtonIndex?: number;
destructiveButtonIndex?: number;
}
// for Android or Web
export interface ActionSheetOptions extends ActionSheetIOSOptions {
icons?: React.ReactNode[];
tintIcons?: boolean;
textStyle?: TextStyle;
titleTextStyle?: TextStyle;
messageTextStyle?: TextStyle;
showSeparators?: boolean;
separatorStyle?: ViewStyle;
}
-13
View File
@@ -1,13 +0,0 @@
{
"include": ["src"],
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"moduleResolution": "node",
"lib": ["es2015", "es2016", "esnext"],
"jsx": "react-native",
"esModuleInterop": true
},
"exclude": ["node_modules"]
}
+39
View File
@@ -0,0 +1,39 @@
import {
ActionSheetProps,
ActionSheetProvider,
connectActionSheet
} from "./index";
import * as React from "react";
interface ChildOwnProps {
name: string;
}
type ChildProps = ChildOwnProps & ActionSheetProps;
class Child extends React.Component<ChildProps> {
showMore = () => {
this.props.showActionSheetWithOptions(
{ options: ["Delete"] },
buttonIndex => {
console.log(buttonIndex);
}
);
};
render() {
return this.props.name;
}
}
const EnhancedChild = connectActionSheet(Child);
class App extends React.Component {
render() {
return (
<ActionSheetProvider>
<EnhancedChild name="lool" />
</ActionSheetProvider>
);
}
}
+3099 -4533
View File
File diff suppressed because it is too large. Load diff