Skip to content

Commit 38fae82

Browse files
authored
feat(ActionSheet): Add new component. (#205)
* 编写选项卡组件 * 类型名称请调整,添加组件文档在网站上展示 * #162修复报错 * fix:#162修复报错 * SpeedDial 悬浮标记 * 优化类型 * feat(ActionSheet): Add new component.
1 parent d3f569e commit 38fae82

File tree

9 files changed

+352
-0
lines changed

9 files changed

+352
-0
lines changed

example/examples/src/routes.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,4 +403,12 @@ export const stackPageData: Routes[] = [
403403
description: '可折叠卡片列表',
404404
},
405405
},
406+
{
407+
name: 'ActionSheet',
408+
component: require('./routes/ActionSheet').default,
409+
params: {
410+
title: 'ActionSheet 动作面板',
411+
description: '该组件提供了一种动作面板, 底部缓缓出现',
412+
},
413+
},
406414
];
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React, { Component } from 'react';
2+
import { StyleSheet, View, } from 'react-native';
3+
import Layout, { Container } from '../../Layout';
4+
import { ActionSheet, Button, ActionSheetItem, Toast, S } from '@uiw/react-native';
5+
import { ComProps } from '../../routes';
6+
7+
const { Header, Body, Card, Footer } = Layout;
8+
9+
export interface IndexProps extends ComProps { }
10+
export interface IndexState {
11+
visible: boolean;
12+
}
13+
14+
export default class Index extends Component<IndexProps, IndexState> {
15+
static state: IndexState;
16+
constructor(props: IndexProps) {
17+
super(props);
18+
this.state = {
19+
visible: false,
20+
};
21+
}
22+
onOpen = () => {
23+
this.setState({ visible: true });
24+
}
25+
onCancel = () => {
26+
this.setState({ visible: false });
27+
};
28+
render() {
29+
const { route } = this.props;
30+
const description = route.params.description;
31+
const title = route.params.title;
32+
return (
33+
<Container>
34+
<Layout>
35+
<Header title={title} description={description} />
36+
<Body>
37+
<View style={styles.divider} />
38+
<Button onPress={this.onOpen}>打开 ActionSheet</Button>
39+
<ActionSheet
40+
visible={this.state.visible}
41+
// onCancel={true}
42+
>
43+
<ActionSheetItem onPress={() => Toast.info('你点击了按钮一', 2, 'info')}>按钮一</ActionSheetItem>
44+
<ActionSheetItem onPress={() => Toast.info('你点击了按钮二', 2, 'info')}>按钮二</ActionSheetItem>
45+
<ActionSheetItem onPress={this.onCancel}>按钮三</ActionSheetItem>
46+
</ActionSheet>
47+
</Body>
48+
<Footer />
49+
</Layout>
50+
</Container>
51+
);
52+
}
53+
}
54+
55+
const styles = StyleSheet.create({
56+
card: {
57+
backgroundColor: '#fff',
58+
paddingLeft: 20,
59+
paddingRight: 20,
60+
},
61+
divider: {
62+
marginVertical: 10,
63+
},
64+
});
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
ActionSheet 动作面板
2+
---
3+
4+
该组件提供了一种动作面板, 底部缓缓出现
5+
6+
## 基础示例
7+
8+
```jsx
9+
import { Fragment, useState } from 'react';
10+
import { ActionSheet, Button,ActionSheetItem } from '@uiw/react-native';
11+
function Demo() {
12+
const [visible, setVisible] = useState(false)
13+
return (
14+
<Fragment>
15+
<Button onPress={()=>setVisible(true)}>打开 ActionSheet</Button>
16+
<ActionSheet
17+
visible={visible}
18+
>
19+
<ActionSheetItem onPress={()=>console.log('按钮一')}>按钮一</ActionSheetItem>
20+
<ActionSheetItem onPress={()=>console.log('按钮二')}>按钮二</ActionSheetItem>
21+
</ActionSheet>
22+
</Fragment>
23+
);
24+
}
25+
```
26+
27+
## 弹层关闭 && 自定义取消文本
28+
29+
```jsx
30+
import { Fragment, useState } from 'react';
31+
import { ActionSheet, Button,ActionSheetItem } from '@uiw/react-native';
32+
function Demo() {
33+
const [visible, setVisible] = useState(false)
34+
return (
35+
<Fragment>
36+
<Button onPress={()=>setVisible(true)}>打开 ActionSheet</Button>
37+
<ActionSheet
38+
visible={visible}
39+
onCancel={true}
40+
cancelText='取消'
41+
>
42+
<ActionSheetItem onPress={()=>console.log('按钮一')}>按钮一</ActionSheetItem>
43+
<ActionSheetItem onPress={()=>console.log('按钮二')}>按钮二</ActionSheetItem>
44+
</ActionSheet>
45+
</Fragment>
46+
);
47+
}
48+
```
49+
50+
## Props
51+
52+
```ts
53+
import { ModalProps } from 'react-native';
54+
55+
export interface ActionSheetProps extends ModalProps {
56+
/** 点击蒙层是否关闭 */
57+
onCancel?: Boolean,
58+
/** 是否展示元素 */
59+
visible: Boolean,
60+
/** 取消的文本 */
61+
cancelText?: React.ReactNode
62+
}
63+
```
64+
65+
## ActionSheetItem Props
66+
67+
```ts
68+
export interface ActionSheetProps extends ModalProps {
69+
/** 点击 Item 触发的事件 */
70+
onPress?: ((event: GestureResponderEvent) => void),
71+
}
72+
```
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
import React from 'react';
2+
import { View, Dimensions, StyleSheet, Text, TouchableOpacity, Modal, ModalProps, Animated } from 'react-native';
3+
import Item from './item'
4+
export { default as ActionSheetItem } from './item';
5+
6+
let MainWidth = Dimensions.get('window').width;
7+
let MainHeight = Dimensions.get('window').height;
8+
export interface ActionSheetProps extends ModalProps {
9+
/** 点击蒙层是否关闭 */
10+
onCancel?: Boolean,
11+
/** 取消的文本 */
12+
cancelText?: React.ReactNode
13+
}
14+
15+
interface ActionSheetState {
16+
animatedHeight: number,
17+
stateVisible: boolean
18+
}
19+
20+
export default class ActionSheet extends React.Component<ActionSheetProps, ActionSheetState> {
21+
private fadeAnim: Animated.Value = new Animated.Value(0);
22+
private animatedRef: React.RefObject<View> = React.createRef()
23+
constructor(props: ActionSheetProps) {
24+
super(props)
25+
this.state = {
26+
animatedHeight: 0,
27+
stateVisible: false
28+
}
29+
}
30+
31+
onClose = () => {
32+
Animated.timing(this.fadeAnim, {
33+
toValue: 0,
34+
duration: 150,
35+
useNativeDriver: true,
36+
}).start(({ finished }) => {
37+
this.setState({ stateVisible: false })
38+
});
39+
}
40+
UNSAFE_componentWillReceiveProps(nextProps: ActionSheetProps) {
41+
if (nextProps.visible) {
42+
this.setState({ stateVisible: true })
43+
Animated.timing(this.fadeAnim, {
44+
toValue: 0,
45+
duration: 0,
46+
useNativeDriver: true,
47+
}).start(({ finished }) => {
48+
this.animatedRef.current &&
49+
this.animatedRef.current.measure((_frameOffsetX, _frameOffsetY, _width, _height, pageOffsetX, pageOffsetY) => {
50+
this.setState({ animatedHeight: _height }, () => {
51+
Animated.timing(this.fadeAnim, {
52+
toValue: -_height,
53+
duration: 150,
54+
useNativeDriver: true,
55+
}).start();
56+
})
57+
})
58+
});
59+
}else {
60+
this.onClose()
61+
}
62+
}
63+
render() {
64+
const { children, visible, cancelText = '取消', onCancel, ...other } = this.props
65+
const { stateVisible } = this.state
66+
if(!stateVisible) {
67+
return null
68+
}
69+
return (
70+
<Modal
71+
animationType="fade" // slide none fade
72+
transparent={true}
73+
visible={stateVisible}
74+
onRequestClose={this.onClose}
75+
{...other}
76+
>
77+
78+
<TouchableOpacity activeOpacity={1} style={[styles.position, styles.spread]} onPress={()=>onCancel&&this.onClose()}>
79+
<Animated.View style={[styles.spread, styles.backdrop]}>
80+
</Animated.View>
81+
</TouchableOpacity>
82+
<Animated.View
83+
style={[
84+
styles.actionSheet,
85+
{ bottom: -this.state.animatedHeight, transform: [{ translateY: this.fadeAnim }] }
86+
]}
87+
ref={this.animatedRef}
88+
>
89+
{
90+
React.Children.toArray(children).map((item, index) => (<View key={index}>
91+
{index !== 0 && <View style={styles.actionSheetItemDivider} />}{item}
92+
</View>))
93+
}
94+
<View style={styles.divider} />
95+
{typeof cancelText !== 'object' ? <TouchableOpacity activeOpacity={1} onPress={this.onClose}>
96+
<View style={styles.actionSheetItem}>
97+
<Text style={styles.actionSheetItemText}>{cancelText}</Text>
98+
</View>
99+
</TouchableOpacity> : <View>{cancelText}</View>
100+
}
101+
</Animated.View>
102+
</Modal>
103+
);
104+
}
105+
}
106+
107+
const styles = StyleSheet.create({
108+
position: {
109+
position: 'absolute',
110+
backgroundColor: 'transparent',
111+
top: 0,
112+
bottom: 0,
113+
left: 0,
114+
right: 0,
115+
zIndex: 9998,
116+
},
117+
backdrop: {
118+
backgroundColor: '#000',
119+
opacity: .2
120+
},
121+
spread: {
122+
width: MainWidth,
123+
height: MainHeight
124+
},
125+
actionSheet: {
126+
width: MainWidth,
127+
position: 'absolute',
128+
left: 0,
129+
right: 0,
130+
backgroundColor: '#fff',
131+
zIndex: 9999
132+
},
133+
divider: {
134+
backgroundColor: 'rgba(197,217,232,.3)',
135+
width: MainWidth,
136+
height: 6
137+
},
138+
actionSheetItemDivider: {
139+
borderBottomColor: 'rgba(197,217,232,.3)',
140+
borderBottomWidth: 2,
141+
width: MainWidth,
142+
},
143+
actionSheetItem: {
144+
width: MainWidth,
145+
height: 50,
146+
justifyContent: 'center',
147+
alignItems: 'center',
148+
},
149+
actionSheetItemText: {
150+
fontSize: 20,
151+
fontWeight: '400',
152+
}
153+
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React from 'react';
2+
import { View, Dimensions, StyleSheet, Text,TouchableOpacity, GestureResponderEvent } from 'react-native';
3+
4+
let MainWidth = Dimensions.get('window').width;
5+
export interface ActionSheetItemProps {
6+
onPress?: ((event: GestureResponderEvent) => void),
7+
}
8+
9+
export interface ActionSheetItemState {
10+
11+
}
12+
13+
export default class ActionSheetItem extends React.Component<ActionSheetItemProps, ActionSheetItemState> {
14+
15+
render() {
16+
const { onPress=()=>{}, children } = this.props
17+
return (
18+
<TouchableOpacity activeOpacity={1} onPress={onPress}>
19+
<View style={styles.actionSheetItem} >
20+
<Text style={styles.actionSheetItemText}>{children}</Text>
21+
</View>
22+
</TouchableOpacity>
23+
);
24+
}
25+
}
26+
27+
const styles = StyleSheet.create({
28+
actionSheetItem: {
29+
width: MainWidth,
30+
height: 50,
31+
justifyContent: 'center',
32+
alignItems: 'center',
33+
},
34+
actionSheetItemText: {
35+
fontSize: 20,
36+
fontWeight: '400',
37+
}
38+
});

packages/core/src/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ export * from './Tooltip';
9595
export { default as CardCollapse } from './CardCollapse';
9696
export * from './CardCollapse';
9797

98+
export { default as ActionSheet } from './ActionSheet';
99+
export * from './ActionSheet';
98100
/**
99101
* Typography
100102
*/
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Markdown, { importAll } from '../../../component/Markdown';
2+
3+
export default class Page extends Markdown {
4+
path = '/packages/core/src/ActionSheet/README.md';
5+
getMarkdown = async () => {
6+
const md = await import('@uiw/react-native/lib/ActionSheet/README.md');
7+
// 支持 markdown 中,相对于当前 index.tsx 相对路径引入图片资源
8+
importAll((require as any).context('./', true, /\.(png|gif|jpg|svg)$/), this.imageFiles);
9+
return md.default || md;
10+
};
11+
}

website/src/routes/menus.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export const componentMenus: MenuData[] = [
5454
{ path: '/components/transitionImage', name: 'TransitionImage 图片过渡' },
5555
{ path: '/components/progress', name: 'Progress 进度条' },
5656
{ path: '/components/tooltip', name: 'Tooltip 工具提示' },
57+
{ path: '/components/actionSheet', name: 'ActionSheet 动作面板' },
5758
{ divider: true, name: '其它' },
5859
{ href: 'https://github.com/uiwjs/react-native-alipay', name: 'Alipay 支付宝', target: '__blank' },
5960
{

website/src/routes/router.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,7 @@ export const getRouterData = {
211211
'/components/cardcollapse': {
212212
component: dynamicWrapper([], () => import('../pages/components/cardcollapse')),
213213
},
214+
'/components/actionSheet': {
215+
component: dynamicWrapper([], () => import('../pages/components/actionSheet')),
216+
},
214217
};

0 commit comments

Comments
 (0)