Skip to content

Commit

Permalink
Merge pull request #4 from TDesignOteam/dev
Browse files Browse the repository at this point in the history
feat(ActionSheet): 添加actionsheet组件
  • Loading branch information
yatessss authored Feb 6, 2024
2 parents 225ce96 + 933a054 commit e80319e
Show file tree
Hide file tree
Showing 8 changed files with 368 additions and 0 deletions.
4 changes: 4 additions & 0 deletions example/src/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
{
"title": "消息提醒",
"children": [
{
"title": "ActionSheet 动作面板",
"key": "ActionSheet"
},
{
"title": "Dialog 对话框",
"key": "Dialog"
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"react-native": "0.71.3",
"react-native-gesture-handler": "^2.14.0",
"react-native-reanimated": "^3.0.2",
"react-native-safe-area-context": "^4.9.0",
"tdesign-icons-react-native": "^0.0.1"
},
"devDependencies": {
Expand Down
7 changes: 7 additions & 0 deletions src/components/ActionSheet/ActionSheet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
:: BASE_DOC ::

## API
### Base Props

临时md,后期会自动生成

140 changes: 140 additions & 0 deletions src/components/ActionSheet/ActionSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import React, { useCallback, useContext } from 'react';
import { StyleSheet } from 'react-native';
import { isString } from 'lodash';
import { SafeAreaInsetsContext } from 'react-native-safe-area-context';
import { View, Text } from '../Base';
import { Touchable } from '../Touchable';
import { Popup } from '../Popup';
import { ThemeContext } from '../../theme';
import { ActionSheetProps, ActionSheetStaticProps, ActionSheetItem, ActionSheetItemTheme } from './types';

const styles = StyleSheet.create({
container: {},
});

export const ActionSheet: React.FunctionComponent<ActionSheetProps> & ActionSheetStaticProps = (props) => {
const { style, title, align, items, onSelected, cancelText, hideCancel } = props;
const { theme } = useContext(ThemeContext);
const insets = useContext(SafeAreaInsetsContext);

const renderTitle = useCallback(() => {
if (isString(title)) {
return (
<View className="bg px16 py8">
<Text className={`text4 ${align === 'left' ? '' : 'textCenter'}`}>{title}</Text>
</View>
);
}
if (React.isValidElement(title)) {
return title;
}
return null;
}, [title, align]);

const getColor = useCallback(
(itemTheme?: ActionSheetItemTheme) => {
switch (itemTheme) {
case 'success':
return theme.colors.success5;
case 'error':
return theme.colors.error6;
case 'warning':
return theme.colors.warning5;
case 'info':
return theme.colors.brand7;
default:
return theme.colors.fontGray1;
}
},
[theme],
);

const renderCancel = useCallback(() => {
return hideCancel ? null : (
<Touchable onPress={() => ActionSheet.hide(ActionSheet.actionSheetId)}>
<View
className="mt4 bg flexCenter"
style={{
height: 50 + (insets?.bottom || 0),
paddingBottom: insets?.bottom,
}}
>
<Text className="bg text3 textCenter">{cancelText}</Text>
</View>
</Touchable>
);
}, [cancelText, hideCancel, insets]);

const onItemPress = (item: ActionSheetItem, index: number) => {
onSelected?.(item, index);
};

return (
<View className="bgPage" style={[styles.container, style]}>
{renderTitle()}
{items?.map((item, index) => (
<Touchable key={index} disabled={!!item.disable} onPress={() => onItemPress(item, index)}>
<View
className={`bt1 px16 bg ${align === 'left' ? 'flexStartH flexCenterH' : 'flexCenter'}`}
style={{ height: 50 }}
>
<Text
className="text3"
style={{
color: getColor(item.theme),
paddingBottom: !hideCancel && index === items.length - 1 ? 0 : insets?.bottom || 0,
}}
>
{item.text}
</Text>
{item.tip ? (
<Text
className="textCenter"
style={{
fontSize: theme.fontSize.base,
color: theme.colors.fontGray2,
}}
>
{item.tip}
</Text>
) : null}
</View>
</Touchable>
))}
{renderCancel()}
</View>
);
};

ActionSheet.defaultProps = {
align: 'center',
cancelText: '取消',
closeOnOverlay: true,
};

ActionSheet.show = (config) => {
// 整体内容高度 + 安全距离
const offsetY = (config.title ? 38 : 0) + (config?.items?.length ? config.items.length * 50 : 0) + 50 + 40;
const mergedConfig = { ...ActionSheet.defaultProps, ...config };

ActionSheet.actionSheetId = Popup.show(<ActionSheet {...mergedConfig} />, {
wrapperStyle: {
height: 'auto',
borderTopRightRadius: 8,
borderTopLeftRadius: 8,
overflow: 'hidden',
},
placement: 'bottom',
animation: {
from: { transform: [{ translateY: offsetY }] },
to: { transform: [{ translateY: 0 }] },
},
closeOnOverlay: mergedConfig.closeOnOverlay,
showOverlay: true,
});
return ActionSheet.actionSheetId;
};

ActionSheet.hide = (id) => {
return Popup.hide(id);
};
156 changes: 156 additions & 0 deletions src/components/ActionSheet/_example/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/**
* title: ActionSheet 动作面板
* description: 由用户操作后触发的一种特定的模态弹出框 ,呈现一组与当前情境相关的两个或多个选项。
* spline: base
* isComponent: true
* toc: false
*/
import { View, ActionSheet, Button } from 'tdesign-react-native/components';
import { Section, CodeSpace, H3, P } from '@src/../example/src/components';

const Demo = () => {
return (
<>
<Section>
<P>用于展示用户头像信息,除了纯展示也可点击进入个人详情等操作。</P>
<H3>1.类型</H3>
<CodeSpace>
<View className="gapY10">
<Button
content={'常规使用'}
onPress={() => {
const id = ActionSheet.show({
title: '常规使用',
items: [{ text: '第一项' }, { text: '第二项' }, { text: '第三项' }, { text: '第四项' }],
onSelected: (item) => {
console.log(item);
ActionSheet.hide(id);
},
});
}}
/>
<Button
content={'选项说明'}
onPress={() => {
const id = ActionSheet.show({
title: '选项说明',
items: [
{ text: '第一项', theme: 'info' },
{ text: '第二项', theme: 'info' },
{ text: '第三项', theme: 'error', tip: '说明' },
{ text: '第四项', theme: 'info', tip: '说明' },
],
onSelected: (item) => {
console.log(item);
ActionSheet.hide(id);
},
});
}}
/>
<Button
content={'隐藏取消按钮'}
onPress={() => {
const id = ActionSheet.show({
hideCancel: true,
title: '隐藏取消按钮',
items: [{ text: '第一项' }, { text: '第二项' }, { text: '第三项' }, { text: '第四项' }],
onSelected: (item) => {
console.log(item);
ActionSheet.hide(id);
},
});
}}
/>
<Button
content={'取消按钮文字自定义'}
onPress={() => {
const id = ActionSheet.show({
cancelText: '自定义',
title: '取消按钮文字自定义',
items: [{ text: '第一项' }, { text: '第二项' }, { text: '第三项' }, { text: '第四项' }],
onSelected: (item) => {
console.log(item);
ActionSheet.hide(id);
},
});
}}
/>
<Button
content={'点击蒙层不关闭'}
onPress={() => {
const id = ActionSheet.show({
closeOnOverlay: false,
title: '点击蒙层不关闭',
items: [{ text: '第一项' }, { text: '第二项' }, { text: '第三项' }, { text: '第四项' }],
onSelected: (item) => {
console.log(item);
ActionSheet.hide(id);
},
});
}}
/>
</View>
</CodeSpace>
</Section>
<Section>
<H3>2.样式</H3>
<CodeSpace>
<View className="gapY10">
<Button
content={'选项禁用'}
onPress={() => {
const id = ActionSheet.show({
title: '选项禁用',
items: [
{ text: '第一项' },
{ text: '第二项', disable: true },
{ text: '第三项', disable: true },
{ text: '第四项' },
],
onSelected: (item) => {
console.log(item);
ActionSheet.hide(id);
},
});
}}
/>
<Button
content={'选项主题'}
onPress={() => {
const id = ActionSheet.show({
title: '选项主题',
items: [
{ text: '第一项', theme: 'warning' },
{ text: '第二项', theme: 'error' },
{ text: '第三项', theme: 'success' },
{ text: '第四项', theme: 'info' },
],
onSelected: (item) => {
console.log(item);
ActionSheet.hide(id);
},
});
}}
/>
<Button
content={'选项左对齐'}
onPress={() => {
const id = ActionSheet.show({
align: 'left',
title: '选项左对齐',
items: [{ text: '第一项' }, { text: '第二项' }, { text: '第三项' }, { text: '第四项' }],
onSelected: (item) => {
console.log(item);
ActionSheet.hide(id);
},
});
}}
/>
</View>
</CodeSpace>
</Section>
</>
);
};

export default Demo;
2 changes: 2 additions & 0 deletions src/components/ActionSheet/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { ActionSheet } from './ActionSheet';
export type { ActionSheetProps } from './types';
57 changes: 57 additions & 0 deletions src/components/ActionSheet/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import type { ViewStyle } from 'react-native';
import { ReactElement } from 'react';

export type ActionSheetItemTheme = 'info' | 'success' | 'warning' | 'error';

export type ActionSheetItem = {
tip?: string;
text?: string;
theme?: ActionSheetItemTheme;
disable?: boolean;
[key: string]: any;
};

export type ActionSheetProps = {
/**
* style
*/
style?: ViewStyle;
/**
* 水平对齐方式
* @default center
*/
align?: 'center' | 'left';
/**
* 取消按钮的文本
* @default 取消
*/
cancelText?: string;
/**
* 是否显示取消按钮
* @default true
*/
hideCancel?: boolean;
/**
* 动作面板描述
*/
title?: string | ReactElement | null;
/**
* 点击蒙层关闭
* @default true
*/
closeOnOverlay?: boolean;
/**
* 菜单列表
*/
items: ActionSheetItem[];
/**
* 点击菜单某项回调
*/
onSelected?: (item?: ActionSheetItem, index?: number) => void;
};

export type ActionSheetStaticProps = {
actionSheetId?: number;
show: (config: ActionSheetProps) => number;
hide: (id?: number) => Promise<void>;
};
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ export * from './Toast';
export * from './Dialog';
export * from './Avatar';
export * from './Image';
export * from './ActionSheet';

0 comments on commit e80319e

Please sign in to comment.