├── .eslintrc.js
├── .gitignore
├── .npmignore
├── .travis.yml
├── ActionSheetContainer.js
├── LICENSE
├── README.md
├── index.d.ts
├── index.js
├── package.json
└── resource
├── Android-1-L.jpeg
├── Android-1-P.jpeg
├── Android-2-L.jpeg
├── Android-2-P.jpeg
├── iOS-1-L.png
├── iOS-1-P.png
├── iOS-2-L.png
└── iOS-2-P.png
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | "automatic",
4 | ],
5 | extends: [
6 | "plugin:automatic/javascript-react-native",
7 | ],
8 | };
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | package-lock.json
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /resource
2 | .travis.yml
3 | .eslintrc.js
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "10"
4 | os:
5 | - linux
6 |
7 | stages:
8 | - test
9 | - name: deploy
10 | if: tag IS present
11 |
12 | jobs:
13 | include:
14 | - stage: test
15 | - stage: deploy
16 | deploy:
17 | provider: npm
18 | email: gaoxiaosong06@gmail.com
19 | api_key: "$NPM_TOKEN"
20 | on:
21 | tags: true
22 |
--------------------------------------------------------------------------------
/ActionSheetContainer.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Dimensions, Modal, ScrollView, StyleSheet, Text, TouchableHighlight, TouchableWithoutFeedback, View } from 'react-native';
3 | import { getSafeAreaInset, isLandscape } from 'react-native-safe-area-utility';
4 |
5 | export default class extends React.PureComponent {
6 | static defaultProps = {
7 | backgroundColor: 'rgba(0, 0, 0, 0.3)',
8 | contentBackgroundColor: '#f9f9f9',
9 | separatorColor: '#d7d7d7',
10 | fontSize: 18,
11 | color: '#007aff',
12 | titleStyle: {
13 | fontSize: 15,
14 | fontWeight: 'bold',
15 | color: '#8f8f8f',
16 | },
17 | messageStyle: {
18 | fontSize: 15,
19 | color: '#8f8f8f',
20 | },
21 | destructiveButtonStyle: {
22 | color: '#d11f1f',
23 | },
24 | cancelButtonStyle: {
25 | fontWeight: 'bold',
26 | },
27 | touchableUnderlayColor: '#dddddd',
28 | supportedOrientations: ['portrait', 'landscape'],
29 | };
30 |
31 | constructor(props) {
32 | super(props);
33 | this.onWindowChange = this._onWindowChange.bind(this);
34 | this.state = {
35 | isLandscape: isLandscape(),
36 | };
37 | }
38 |
39 | componentDidMount() {
40 | Dimensions.addEventListener('change', this.onWindowChange);
41 | }
42 |
43 | componentWillUnmount() {
44 | Dimensions.removeEventListener('change', this.onWindowChange);
45 | }
46 |
47 | render() {
48 | const { config, backgroundColor, supportedOrientations } = this.props;
49 | const { cancelButtonIndex } = config;
50 | const closeFunc = this._click.bind(this, cancelButtonIndex);
51 | return (
52 |
53 |
60 |
61 |
62 |
63 | {this._renderSections()}
64 |
65 |
66 | );
67 | }
68 |
69 | _renderSections = () => {
70 | const {width, height} = Dimensions.get('window');
71 | const inset = getSafeAreaInset();
72 | const { config } = this.props;
73 | const { title, message, options, cancelButtonIndex } = config;
74 | const contentStyle = {
75 | paddingHorizontal: 10,
76 | marginBottom: inset.bottom > 0 ? inset.bottom : 10,
77 | marginTop: this.state.isLandscape ? inset.top + 10 : inset.top + 44,
78 | };
79 | contentStyle.maxHeight = height - contentStyle.marginBottom - contentStyle.marginTop;
80 | if (this.state.isLandscape) {
81 | contentStyle.width = Math.max(width / 3, height - 10 * 2);
82 | contentStyle.alignSelf = 'center';
83 | }
84 | const section = [];
85 | let cancelView = null;
86 | (title || message) && section.push(this._renderTitle(title, message));
87 | options.forEach((item, index) => {
88 | const itemView = this._renderItem(item, index);
89 | if (index === cancelButtonIndex) {
90 | cancelView = itemView;
91 | } else {
92 | section.push(itemView);
93 | }
94 | });
95 | const sections = cancelView ? [section, cancelView] : [section];
96 | return (
97 |
98 | {sections.map((sectionItem, index) => {
99 | const style = index > 0 ? {
100 | marginTop: 9,
101 | } : {};
102 | return this._renderSection(sectionItem, index, style);
103 | })}
104 |
105 | );
106 | };
107 |
108 | _renderTitle = (title, message) => {
109 | const { titleStyle, messageStyle } = this.props;
110 | const style = {marginVertical: 6};
111 | return (
112 |
113 | {title && (
114 |
115 | {title}
116 |
117 | )}
118 | {message && (
119 |
120 | {message}
121 |
122 | )}
123 |
124 | );
125 | };
126 |
127 | _renderItem = (item, index) => {
128 | const { config, destructiveButtonStyle, cancelButtonStyle, touchableUnderlayColor, fontSize, color } = this.props;
129 | const { destructiveButtonIndex, cancelButtonIndex } = config;
130 | const isCancel = index === cancelButtonIndex;
131 | const isDestructive = index === destructiveButtonIndex;
132 | const textStyle = isCancel ? cancelButtonStyle :
133 | isDestructive ? destructiveButtonStyle : null;
134 | return (
135 |
141 |
142 |
143 | {item}
144 |
145 |
146 |
147 | );
148 | };
149 |
150 | _renderSection(items, index, style) {
151 | const {contentBackgroundColor: backgroundColor} = this.props;
152 | const isArray = Array.isArray(items);
153 | const Component = isArray ? ScrollView : View;
154 | const props = isArray ? {
155 | bounces: false,
156 | } : {};
157 | return (
158 |
163 | {isArray ? items.map((item, innerIndex) => {
164 | const views = [item];
165 | if (innerIndex < items.length - 1) {
166 | views.push(this._renderSeparatorLine('sepline' + innerIndex));
167 | }
168 | return views;
169 | }) : items}
170 |
171 | );
172 | }
173 |
174 | _renderSeparatorLine(key) {
175 | const {separatorColor: backgroundColor} = this.props;
176 | return (
177 |
181 | );
182 | }
183 |
184 | _click(index) {
185 | this.props.callback && this.props.callback(index);
186 | }
187 |
188 | _onWindowChange() {
189 | this.setState({isLandscape: isLandscape()});
190 | }
191 | }
192 |
193 | const styles = StyleSheet.create({
194 | view: {
195 | position: 'absolute',
196 | top: 0,
197 | left: 0,
198 | right: 0,
199 | bottom: 0,
200 | opacity: 1,
201 | },
202 | touchview: {
203 | flex: 1,
204 | backgroundColor: 'transparent',
205 | },
206 | content: {
207 | flex: 0,
208 | },
209 | section: {
210 | borderRadius: 11,
211 | overflow: 'hidden',
212 | },
213 | seperator: {
214 | height: StyleSheet.hairlineWidth,
215 | },
216 | title: {
217 | justifyContent: 'center',
218 | alignItems: 'center',
219 | paddingVertical: 10,
220 | },
221 | button: {
222 | alignItems: 'center',
223 | justifyContent: 'center',
224 | paddingHorizontal: 16,
225 | height: 57,
226 | },
227 | buttonView: {
228 | justifyContent: 'center',
229 | alignItems: 'center',
230 | },
231 | });
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Xiaosong Gao
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-general-actionsheet
2 |
3 | [](https://www.npmjs.com/package/react-native-general-actionsheet)
4 | [](https://travis-ci.org/gaoxiaosong/react-native-general-actionsheet)
5 |
6 | [中文说明](https://www.jianshu.com/p/2377cca9a58c)
7 |
8 | This is a general ActionSheet api. You can use [ActionSheetIOS](https://facebook.github.io/react-native/docs/actionsheetios) in iOS and use a custom view in Android. Or you can use custom view in both iOS and Android.
9 |
10 | It only support `ActionSheet.showActionSheetWithOptions` now.
11 |
12 | ## ScreenShots
13 |
14 | ### Portrait
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | ### Landscape
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | ## Install
47 |
48 | Install by Yarn:
49 |
50 | ```shell
51 | yarn add react-native-general-actionsheet
52 | ```
53 |
54 | Install by NPM:
55 |
56 | ```shell
57 | npm install --save react-native-general-actionsheet
58 | ```
59 |
60 | ## Usage
61 |
62 | Use the module in file:
63 |
64 | ```javascript
65 | import ActionSheet from 'react-native-general-actionsheet';
66 |
67 | ActionSheet.showActionSheetWithOptions(options, callback);
68 | ```
69 |
70 | Parameters `options` and `callback` is same as [ActionSheetIOS](https://facebook.github.io/react-native/docs/actionsheetios).
71 |
72 | ## Use `ActionSheetIOS`
73 |
74 | You can change using `ActionSheetIOS` or not globally:
75 |
76 | ```javascript
77 | import ActionSheet from 'react-native-general-actionsheet';
78 |
79 | ActionSheet.useActionSheetIOS = true/false;
80 | ```
81 |
82 | ## Customize Style
83 |
84 | You can change style of container globally.
85 |
86 | ```javascript
87 | import ActionSheet from 'react-native-general-actionsheet';
88 |
89 | ActionSheet.Container.defaultProps.xxx = yyy;
90 | ```
91 |
92 | It supports following properties:
93 |
94 | | Name | Type | Description |
95 | | :-: | :-: | :- |
96 | | backgroundColor | string | Background color of whole view |
97 | | contentBackgroundColor | string | Background color of content view |
98 | | separatorColor | string | Separator line color |
99 | | fontSize | number | Button text font size |
100 | | color | string | Button text color |
101 | | titleStyle | object | Style of title text |
102 | | messageStyle | object | Style of message text |
103 | | destructiveButtonStyle | object | Style of destructive button |
104 | | cancelButtonStyle | object | Style of cancel button |
105 | | touchableUnderlayColor | string | Underlay color of button touch action |
106 | | supportedOrientations | array | Supported orientations for iOS |
107 |
--------------------------------------------------------------------------------
/index.d.ts:
--------------------------------------------------------------------------------
1 | import { StyleProp, TextStyle, ViewStyle } from 'react-native';
2 |
3 | declare namespace ActionSheet {
4 | interface StyleProps {
5 | // 整个视图的背景色
6 | backgroundColor: string;
7 | // 内容区的背景色
8 | contentBackgroundColor: string;
9 | // 分隔线的颜色
10 | separatorColor: string;
11 | // 按钮文本的字号
12 | fontSize: number;
13 | // 按钮文本的颜色
14 | color: string;
15 | // 顶部标题的样式
16 | titleStyle: StyleProp;
17 | // 顶部消息的样式
18 | messageStyle: StyleProp;
19 | // 辅助按钮的样式
20 | destructiveButtonStyle: StyleProp;
21 | // 取消按钮的样式
22 | cancelButtonStyle: StyleProp;
23 | // 按钮点击操作的Underlay颜色
24 | touchableUnderlayColor: string;
25 | }
26 | var Container: {
27 | defaultProps: Partial;
28 | };
29 | var useActionSheetIOS: boolean;
30 | function showActionSheetWithOptions(config: { options: string[]; title?: string; message?: string; cancelButtonIndex?: number; }, callback: (index: number) => void): void;
31 | }
32 |
33 | export = ActionSheet;
34 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { ActionSheetIOS, Platform } from 'react-native';
3 | import ActionSheetContainer from './ActionSheetContainer';
4 | import RootSiblings from 'react-native-root-siblings';
5 |
6 | let instance = null;
7 |
8 | const ActionSheet = {
9 | Container: ActionSheetContainer,
10 | useActionSheetIOS: true,
11 | showActionSheetWithOptions: (config, callback) => {
12 | if (Platform.OS === 'ios' && ActionSheet.useActionSheetIOS) {
13 | ActionSheetIOS.showActionSheetWithOptions(config, callback);
14 | return;
15 | }
16 | if (instance) {
17 | return;
18 | }
19 | instance = new RootSiblings(
20 | {
23 | instance && instance.destroy(() => {
24 | instance = null;
25 | setTimeout(() => {
26 | callback && callback(index);
27 | }, 0);
28 | });
29 | }}
30 | />
31 | );
32 | },
33 | };
34 |
35 | export default ActionSheet;
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-general-actionsheet",
3 | "version": "1.0.4",
4 | "private": false,
5 | "description": "ActionSheet api on iOS and Android same as ActionSheetIOS.",
6 | "main": "index.js",
7 | "scripts": {
8 | "test": "./node_modules/.bin/eslint . --ext .js"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "https://github.com/gaoxiaosong/react-native-general-actionsheet.git"
13 | },
14 | "keywords": [
15 | "React Native",
16 | "ActionSheet"
17 | ],
18 | "author": "Xiaosong Gao",
19 | "license": "MIT",
20 | "bugs": {
21 | "url": "https://github.com/gaoxiaosong/react-native-general-actionsheet/issues",
22 | "email": "gaoxiaosong06@gmail.com"
23 | },
24 | "homepage": "https://github.com/gaoxiaosong/react-native-general-actionsheet#readme",
25 | "dependencies": {
26 | "react-native-safe-area-utility": "^1.0.0",
27 | "react-native-root-siblings": "^3.1.6"
28 | },
29 | "devDependencies": {
30 | "eslint-plugin-automatic": "latest",
31 | "react": "latest"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/resource/Android-1-L.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gxsshallot/react-native-general-actionsheet/5b57220dfe97fa4c59ec5edf70e493b65bc50ddb/resource/Android-1-L.jpeg
--------------------------------------------------------------------------------
/resource/Android-1-P.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gxsshallot/react-native-general-actionsheet/5b57220dfe97fa4c59ec5edf70e493b65bc50ddb/resource/Android-1-P.jpeg
--------------------------------------------------------------------------------
/resource/Android-2-L.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gxsshallot/react-native-general-actionsheet/5b57220dfe97fa4c59ec5edf70e493b65bc50ddb/resource/Android-2-L.jpeg
--------------------------------------------------------------------------------
/resource/Android-2-P.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gxsshallot/react-native-general-actionsheet/5b57220dfe97fa4c59ec5edf70e493b65bc50ddb/resource/Android-2-P.jpeg
--------------------------------------------------------------------------------
/resource/iOS-1-L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gxsshallot/react-native-general-actionsheet/5b57220dfe97fa4c59ec5edf70e493b65bc50ddb/resource/iOS-1-L.png
--------------------------------------------------------------------------------
/resource/iOS-1-P.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gxsshallot/react-native-general-actionsheet/5b57220dfe97fa4c59ec5edf70e493b65bc50ddb/resource/iOS-1-P.png
--------------------------------------------------------------------------------
/resource/iOS-2-L.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gxsshallot/react-native-general-actionsheet/5b57220dfe97fa4c59ec5edf70e493b65bc50ddb/resource/iOS-2-L.png
--------------------------------------------------------------------------------
/resource/iOS-2-P.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gxsshallot/react-native-general-actionsheet/5b57220dfe97fa4c59ec5edf70e493b65bc50ddb/resource/iOS-2-P.png
--------------------------------------------------------------------------------