From 59553753b737601592094d9e004e63e68e0c7c45 Mon Sep 17 00:00:00 2001
From: jaywcjlove <398188662@qq.com>
Date: Fri, 22 May 2020 17:24:36 +0800
Subject: [PATCH] feat: Add MaskLayer component.
---
components/MaskLayer/README.md | 63 ++++++++++++++++
components/MaskLayer/index.tsx | 134 +++++++++++++++++++++++++++++++++
components/index.tsx | 2 +
components/utils/index.ts | 1 +
src/Home.tsx | 2 +-
src/routes.tsx | 8 ++
src/routes/MaskLayer/index.tsx | 72 ++++++++++++++++++
src/routes/Modal/index.tsx | 3 +-
8 files changed, 283 insertions(+), 2 deletions(-)
create mode 100644 components/MaskLayer/README.md
create mode 100644 components/MaskLayer/index.tsx
create mode 100644 src/routes/MaskLayer/index.tsx
diff --git a/components/MaskLayer/README.md b/components/MaskLayer/README.md
new file mode 100644
index 000000000..1600fd111
--- /dev/null
+++ b/components/MaskLayer/README.md
@@ -0,0 +1,63 @@
+MaskLayer 遮罩层
+---
+
+用于模态对话框,中的遮罩层。
+
+### 基础示例
+
+
+```jsx
+import React, { Component, Fragment } from 'react';
+import { View, Text, Alert, SafeAreaView } from 'react-native';
+import { Modal, Button, MaskLayer } from '@uiw/react-native';
+
+export default () => {
+ const [visible, setVisible] = useState(true);
+ return (
+
+ {
+ setVisible(false);
+ }}>
+ 展示内容
+
+
+
+ );
+}
+```
+
+
+## Props
+
+继承原生 Modal 属性 [`ModalProps`](https://facebook.github.io/react-native/docs/modal.html#props)
+
+```typescript
+interface MaskLayerProps extends RNModalProps {
+ /**
+ * 遮罩层是否禁止点击
+ * defult: `true`
+ */
+ maskClosable?: boolean;
+ /**
+ * 是否隐藏
+ */
+ visible?: boolean;
+ /**
+ * 遮罩层透明度
+ * defult: `0.6`
+ */
+ opacity?: number;
+ /**
+ * 隐藏消除回调事件
+ */
+ onDismiss?: () => void;
+ children?: JSX.Element;
+}
+```
diff --git a/components/MaskLayer/index.tsx b/components/MaskLayer/index.tsx
new file mode 100644
index 000000000..80ef09c36
--- /dev/null
+++ b/components/MaskLayer/index.tsx
@@ -0,0 +1,134 @@
+import React, {useState, useMemo} from 'react';
+import {
+ Modal,
+ ModalProps as RNModalProps,
+ Animated,
+ TouchableOpacity,
+ StyleSheet,
+} from 'react-native';
+import {usePrevious} from '../utils';
+
+const styles = StyleSheet.create({
+ position: {
+ position: 'absolute',
+ backgroundColor: 'transparent',
+ top: 0,
+ bottom: 0,
+ left: 0,
+ right: 0,
+ zIndex: 9998,
+ },
+ backdrop: {
+ backgroundColor: '#000',
+ },
+ content: {
+ backgroundColor: '#fff',
+ position: 'absolute',
+ },
+});
+
+export interface MaskLayerProps extends RNModalProps {
+ /**
+ * 遮罩层是否禁止点击
+ * defult: `true`
+ */
+ maskClosable?: boolean;
+ /**
+ * 是否隐藏
+ */
+ visible?: boolean;
+ /**
+ * 遮罩层透明度
+ * defult: `0.6`
+ */
+ opacity?: number;
+ /**
+ * 隐藏消除回调事件
+ */
+ onDismiss?: () => void;
+ animatedParallelShow?: Animated.CompositeAnimation[];
+ animatedParallelHide?: Animated.CompositeAnimation[];
+ children?: JSX.Element;
+}
+
+export default (props: MaskLayerProps = {}) => {
+ const {
+ maskClosable = true,
+ children,
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ visible: _vis,
+ opacity = 0.6,
+ onDismiss,
+ animatedParallelShow = [],
+ animatedParallelHide = [],
+ ...otherProps
+ } = props;
+ const [visible, setVisible] = useState(!!props.visible);
+ const preVisible = usePrevious(props.visible);
+ const [visibleModal, setVisibleModal] = useState(false);
+ const [bgOpacity] = useState(new Animated.Value(0));
+ useMemo(() => {
+ if (preVisible !== props.visible && props.visible) {
+ setVisible(!!props.visible);
+ setVisibleModal(false);
+ Animated.parallel([
+ Animated.spring(bgOpacity, {
+ toValue: opacity,
+ overshootClamping: true,
+ useNativeDriver: true,
+ }),
+ ...animatedParallelShow,
+ ]).start();
+ } else if (preVisible !== props.visible && !props.visible) {
+ Animated.parallel([
+ Animated.spring(bgOpacity, {
+ toValue: 0,
+ overshootClamping: true,
+ useNativeDriver: true,
+ }),
+ ...animatedParallelHide,
+ ]).start(() => {
+ setVisible(!!props.visible);
+ setVisibleModal(true);
+ });
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [props.visible]);
+ const backdropContent = (
+
+ );
+ let backdrop = (
+ onDismiss && onDismiss()}>
+ {backdropContent}
+
+ );
+ let isTrue = true;
+
+ if (!visible && visibleModal) {
+ isTrue = false;
+ }
+ return (
+
+ {maskClosable ? backdrop : backdropContent}
+ {children &&
+ React.Children.toArray(children).map((child) => {
+ if (!React.isValidElement(child)) {
+ return;
+ }
+ return React.cloneElement(child, {
+ ...child.props,
+ ...{style: [{zIndex: 9999}, child.props.style]},
+ });
+ })}
+
+ );
+};
diff --git a/components/index.tsx b/components/index.tsx
index 5a81e260a..1c4092672 100644
--- a/components/index.tsx
+++ b/components/index.tsx
@@ -13,6 +13,8 @@ export {default as Icon} from './Icon';
export {default as List} from './List';
export {default as Loader} from './Loader';
export {default as Modal} from './Modal';
+export {default as MaskLayer} from './MaskLayer';
+export * from './MaskLayer';
export {default as Radio} from './Radio';
export {default as Result} from './Result';
export {default as SegmentedControl} from './SegmentedControl';
diff --git a/components/utils/index.ts b/components/utils/index.ts
index 5da263649..b8d1e8dbd 100644
--- a/components/utils/index.ts
+++ b/components/utils/index.ts
@@ -2,3 +2,4 @@ import color from 'color';
import * as colors from './colors';
export {color, colors};
+export * from './hooks';
diff --git a/src/Home.tsx b/src/Home.tsx
index 77c47009c..3bbf2d8df 100644
--- a/src/Home.tsx
+++ b/src/Home.tsx
@@ -41,7 +41,7 @@ const Link = ({
{title || ''}
- {description}
+ {description}
diff --git a/src/routes.tsx b/src/routes.tsx
index f89061979..c31da0cee 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -144,6 +144,14 @@ export const stackPageData: Routes[] = [
description: '模态对话框,React Native 原生组件。',
},
},
+ {
+ name: 'MaskLayer',
+ component: require('./routes/MaskLayer').default,
+ params: {
+ title: 'MaskLayer 遮罩层',
+ description: '用于模态对话框,中的遮罩层。',
+ },
+ },
{
name: 'Radio',
component: require('./routes/Radio').default,
diff --git a/src/routes/MaskLayer/index.tsx b/src/routes/MaskLayer/index.tsx
new file mode 100644
index 000000000..b8ab8d208
--- /dev/null
+++ b/src/routes/MaskLayer/index.tsx
@@ -0,0 +1,72 @@
+import React, {useState} from 'react';
+import {Text, View, StyleSheet} from 'react-native';
+import Layout, {Container} from '../../Layout';
+import {Button, MaskLayer} from '../../../components';
+import {ComProps} from '../../typings';
+
+const {Header, Body, Card, Footer} = Layout;
+
+export interface MenuDropdownProps extends ComProps {}
+
+export default function MenuDropdownView(props: MenuDropdownProps) {
+ const {route} = props || {};
+ const description = route.params.description;
+ const title = route.params.title;
+ const [visible, setVisible] = useState(true);
+ return (
+
+
+
+
+
+ {
+ setVisible(false);
+ }}>
+
+
+ 内容
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ card: {
+ backgroundColor: '#fff',
+ paddingLeft: 20,
+ paddingRight: 20,
+ },
+ color: {
+ color: '#fff',
+ },
+ top: {
+ top: 100,
+ },
+});
diff --git a/src/routes/Modal/index.tsx b/src/routes/Modal/index.tsx
index 13458bd46..808827ca3 100644
--- a/src/routes/Modal/index.tsx
+++ b/src/routes/Modal/index.tsx
@@ -27,11 +27,12 @@ export default class ModalView extends Component {
this.setState({modalVisible: false})}
onRequestClose={() => {
- this.setState({modalVisible: false});
+ // this.setState({modalVisible: false});
// Alert.alert('Modal has been closed.');
}}>