@unif/react-native-design 0.14.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/components/ui/Confirm/ConfirmHost.js +30 -4
- package/lib/module/components/ui/Confirm/ConfirmHost.js.map +1 -1
- package/lib/module/components/ui/Confirm/styles.js +12 -2
- package/lib/module/components/ui/Confirm/styles.js.map +1 -1
- package/lib/typescript/src/components/ui/Confirm/ConfirmHost.d.ts.map +1 -1
- package/lib/typescript/src/components/ui/Confirm/styles.d.ts +10 -2
- package/lib/typescript/src/components/ui/Confirm/styles.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/ui/Confirm/ConfirmHost.tsx +38 -1
- package/src/components/ui/Confirm/styles.ts +12 -2
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
4
|
-
import { Modal, Pressable, Text, View } from 'react-native';
|
|
4
|
+
import { Animated, Easing, Modal, Pressable, Text, View, useWindowDimensions } from 'react-native';
|
|
5
5
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
6
6
|
import { space, useThemedStyles } from "../../../theme/index.js";
|
|
7
7
|
import { Button } from "../Button/index.js";
|
|
8
8
|
import { _subs } from "./confirm.js";
|
|
9
9
|
import { makeStyles } from "./styles.js";
|
|
10
10
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
11
|
+
/** 遮罩淡入 / 淡出时长(ms)—— 出场略长于入场,与 Modal slide 退场(~300ms)大致同步收完。 */
|
|
12
|
+
const SCRIM_IN_MS = 220;
|
|
13
|
+
const SCRIM_OUT_MS = 260;
|
|
14
|
+
|
|
11
15
|
/**
|
|
12
16
|
* Confirm 对话框宿主 —— App 根挂一次,监听 `confirm()` 调用并渲染。
|
|
13
17
|
*
|
|
@@ -18,6 +22,9 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
18
22
|
export function ConfirmHost() {
|
|
19
23
|
const styles = useThemedStyles(makeStyles);
|
|
20
24
|
const insets = useSafeAreaInsets();
|
|
25
|
+
const {
|
|
26
|
+
height: screenH
|
|
27
|
+
} = useWindowDimensions();
|
|
21
28
|
const [entry, setEntry] = useState(null);
|
|
22
29
|
|
|
23
30
|
// 跟踪当前未决 entry,供 unmount cleanup resolve(false)。
|
|
@@ -48,20 +55,39 @@ export function ConfirmHost() {
|
|
|
48
55
|
entry?.resolve(false);
|
|
49
56
|
setEntry(null);
|
|
50
57
|
}, [entry]);
|
|
58
|
+
|
|
59
|
+
// 遮罩自持淡入 / 淡出(不蹭 Modal 的位移动画,理由见下方 scrim 注释)。退场也要淡:
|
|
60
|
+
// entry 置 null 后 Modal 仍在滑出、本层仍挂载,不淡的话遮罩会在滑出末尾硬切消失。
|
|
61
|
+
const scrimOpacity = useRef(new Animated.Value(0)).current;
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
Animated.timing(scrimOpacity, {
|
|
64
|
+
toValue: entry ? 1 : 0,
|
|
65
|
+
duration: entry ? SCRIM_IN_MS : SCRIM_OUT_MS,
|
|
66
|
+
easing: Easing.out(Easing.quad),
|
|
67
|
+
useNativeDriver: true
|
|
68
|
+
}).start();
|
|
69
|
+
}, [entry, scrimOpacity]);
|
|
51
70
|
return /*#__PURE__*/_jsx(Modal, {
|
|
52
71
|
visible: !!entry,
|
|
53
72
|
transparent: true,
|
|
54
73
|
animationType: "slide",
|
|
55
74
|
statusBarTranslucent: true,
|
|
56
75
|
onRequestClose: handleCancel,
|
|
57
|
-
children: /*#__PURE__*/
|
|
76
|
+
children: /*#__PURE__*/_jsxs(Pressable, {
|
|
58
77
|
style: styles.backdrop,
|
|
59
78
|
onPress: handleCancel
|
|
60
79
|
// [M-16] accessible={false}:否则 backdrop 把整个子树合并成单一「关闭, 按钮」,
|
|
61
80
|
// 遮蔽 sheet 内的 title/message/按钮。SR 取消路径走 cancel 按钮 + onRequestClose。
|
|
62
81
|
,
|
|
63
82
|
accessible: false,
|
|
64
|
-
children: /*#__PURE__*/_jsx(
|
|
83
|
+
children: [/*#__PURE__*/_jsx(Animated.View, {
|
|
84
|
+
pointerEvents: "none",
|
|
85
|
+
style: [styles.scrim, {
|
|
86
|
+
top: -screenH,
|
|
87
|
+
opacity: scrimOpacity
|
|
88
|
+
}],
|
|
89
|
+
testID: "confirm-scrim"
|
|
90
|
+
}), /*#__PURE__*/_jsx(Pressable, {
|
|
65
91
|
style: [styles.sheet, {
|
|
66
92
|
paddingBottom: insets.bottom + space['7']
|
|
67
93
|
}],
|
|
@@ -95,7 +121,7 @@ export function ConfirmHost() {
|
|
|
95
121
|
})]
|
|
96
122
|
})]
|
|
97
123
|
}) : null
|
|
98
|
-
})
|
|
124
|
+
})]
|
|
99
125
|
})
|
|
100
126
|
});
|
|
101
127
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","useCallback","useEffect","useRef","useState","Modal","Pressable","Text","View","useSafeAreaInsets","space","useThemedStyles","Button","_subs","makeStyles","jsx","_jsx","jsxs","_jsxs","Fragment","_Fragment","ConfirmHost","styles","insets","entry","setEntry","pendingRef","current","lastEntryRef","display","sub","next","add","delete","resolve","handleConfirm","handleCancel","visible","transparent","animationType","statusBarTranslucent","onRequestClose","children","style","backdrop","onPress","accessible","sheet","paddingBottom","bottom","body","title","accessibilityRole","message","actions","
|
|
1
|
+
{"version":3,"names":["React","useCallback","useEffect","useRef","useState","Animated","Easing","Modal","Pressable","Text","View","useWindowDimensions","useSafeAreaInsets","space","useThemedStyles","Button","_subs","makeStyles","jsx","_jsx","jsxs","_jsxs","Fragment","_Fragment","SCRIM_IN_MS","SCRIM_OUT_MS","ConfirmHost","styles","insets","height","screenH","entry","setEntry","pendingRef","current","lastEntryRef","display","sub","next","add","delete","resolve","handleConfirm","handleCancel","scrimOpacity","Value","timing","toValue","duration","easing","out","quad","useNativeDriver","start","visible","transparent","animationType","statusBarTranslucent","onRequestClose","children","style","backdrop","onPress","accessible","pointerEvents","scrim","top","opacity","testID","sheet","paddingBottom","bottom","body","title","accessibilityRole","message","actions","label","cancelLabel","variant","block","confirmLabel","destructive"],"sourceRoot":"../../../../../src","sources":["components/ui/Confirm/ConfirmHost.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -3,13 +3,23 @@
|
|
|
3
3
|
import { StyleSheet } from 'react-native';
|
|
4
4
|
import { fw, r, space, type as t } from "../../../theme/index.js";
|
|
5
5
|
/** Confirm 对话框 styles —— 原生 Modal(transparent + slide)底部弹出卡片:
|
|
6
|
-
* backdrop
|
|
6
|
+
* backdrop 铺满、点击取消(**不挂半透黑** —— 见 ConfirmHost 的 scrim 注释);
|
|
7
|
+
* scrim 半透黑独立一层;sheet 贴底圆角顶。不依赖 @gorhom。 */
|
|
7
8
|
export const makeStyles = c => StyleSheet.create({
|
|
8
9
|
backdrop: {
|
|
9
10
|
flex: 1,
|
|
10
|
-
backgroundColor: c.scrim,
|
|
11
11
|
justifyContent: 'flex-end'
|
|
12
12
|
},
|
|
13
|
+
// 半透黑遮罩层(纯视觉,不接触控):绝对定位四边贴合 backdrop,`top` 由 ConfirmHost 内联
|
|
14
|
+
// 覆写成 -屏高(向上外扩一屏,盖住 Modal slide 位移期露出的顶部)。
|
|
15
|
+
scrim: {
|
|
16
|
+
position: 'absolute',
|
|
17
|
+
top: 0,
|
|
18
|
+
left: 0,
|
|
19
|
+
right: 0,
|
|
20
|
+
bottom: 0,
|
|
21
|
+
backgroundColor: c.scrim
|
|
22
|
+
},
|
|
13
23
|
sheet: {
|
|
14
24
|
backgroundColor: c.surface,
|
|
15
25
|
borderTopLeftRadius: r(20),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["StyleSheet","fw","r","space","type","t","makeStyles","c","create","backdrop","flex","
|
|
1
|
+
{"version":3,"names":["StyleSheet","fw","r","space","type","t","makeStyles","c","create","backdrop","flex","justifyContent","scrim","position","top","left","right","bottom","backgroundColor","sheet","surface","borderTopLeftRadius","borderTopRightRadius","paddingTop","body","paddingHorizontal","gap","title","fontSize","heroSm","fontWeight","semi","color","foreground","letterSpacing","message","foregroundMuted","lineHeight","actions","flexDirection"],"sourceRoot":"../../../../../src","sources":["components/ui/Confirm/styles.ts"],"mappings":";;AAAA,SAASA,UAAU,QAAQ,cAAc;AACzC,SAASC,EAAE,EAAEC,CAAC,EAAEC,KAAK,EAAEC,IAAI,IAAIC,CAAC,QAAQ,yBAAgB;AAGxD;AACA;AACA;AACA,OAAO,MAAMC,UAAU,GAAIC,CAAc,IACvCP,UAAU,CAACQ,MAAM,CAAC;EAChBC,QAAQ,EAAE;IACRC,IAAI,EAAE,CAAC;IACPC,cAAc,EAAE;EAClB,CAAC;EACD;EACA;EACAC,KAAK,EAAE;IACLC,QAAQ,EAAE,UAAU;IACpBC,GAAG,EAAE,CAAC;IACNC,IAAI,EAAE,CAAC;IACPC,KAAK,EAAE,CAAC;IACRC,MAAM,EAAE,CAAC;IACTC,eAAe,EAAEX,CAAC,CAACK;EACrB,CAAC;EACDO,KAAK,EAAE;IACLD,eAAe,EAAEX,CAAC,CAACa,OAAO;IAC1BC,mBAAmB,EAAEnB,CAAC,CAAC,EAAE,CAAC;IAC1BoB,oBAAoB,EAAEpB,CAAC,CAAC,EAAE,CAAC;IAC3BqB,UAAU,EAAEpB,KAAK,CAAC,GAAG;EACvB,CAAC;EACDqB,IAAI,EAAE;IACJC,iBAAiB,EAAEtB,KAAK,CAAC,GAAG,CAAC;IAC7BuB,GAAG,EAAEvB,KAAK,CAAC,GAAG;EAChB,CAAC;EACDwB,KAAK,EAAE;IACLC,QAAQ,EAAEvB,CAAC,CAACwB,MAAM;IAClBC,UAAU,EAAE7B,EAAE,CAAC8B,IAAI;IACnBC,KAAK,EAAEzB,CAAC,CAAC0B,UAAU;IACnBC,aAAa,EAAE,CAAC;EAClB,CAAC;EACDC,OAAO,EAAE;IACPP,QAAQ,EAAEvB,CAAC,CAACmB,IAAI;IAChBQ,KAAK,EAAEzB,CAAC,CAAC6B,eAAe;IACxBC,UAAU,EAAEhC,CAAC,CAACmB,IAAI,GAAG;EACvB,CAAC;EACDc,OAAO,EAAE;IACPC,aAAa,EAAE,KAAK;IACpBb,GAAG,EAAEvB,KAAK,CAAC,GAAG,CAAC;IACfsB,iBAAiB,EAAEtB,KAAK,CAAC,GAAG,CAAC;IAC7BoB,UAAU,EAAEpB,KAAK,CAAC,GAAG;EACvB;AACF,CAAC,CAAC","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfirmHost.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/Confirm/ConfirmHost.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ConfirmHost.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/Confirm/ConfirmHost.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmD,MAAM,OAAO,CAAC;AAqBxE;;;;;;GAMG;AACH,wBAAgB,WAAW,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAmHtD"}
|
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import type { ColorTokens } from '../../../theme';
|
|
2
2
|
/** Confirm 对话框 styles —— 原生 Modal(transparent + slide)底部弹出卡片:
|
|
3
|
-
* backdrop
|
|
3
|
+
* backdrop 铺满、点击取消(**不挂半透黑** —— 见 ConfirmHost 的 scrim 注释);
|
|
4
|
+
* scrim 半透黑独立一层;sheet 贴底圆角顶。不依赖 @gorhom。 */
|
|
4
5
|
export declare const makeStyles: (c: ColorTokens) => Readonly<{
|
|
5
6
|
backdrop: {
|
|
6
7
|
flex: number;
|
|
7
|
-
backgroundColor: string;
|
|
8
8
|
justifyContent: "flex-end";
|
|
9
9
|
};
|
|
10
|
+
scrim: {
|
|
11
|
+
position: "absolute";
|
|
12
|
+
top: number;
|
|
13
|
+
left: number;
|
|
14
|
+
right: number;
|
|
15
|
+
bottom: number;
|
|
16
|
+
backgroundColor: string;
|
|
17
|
+
};
|
|
10
18
|
sheet: {
|
|
11
19
|
backgroundColor: string;
|
|
12
20
|
borderTopLeftRadius: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/Confirm/styles.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;
|
|
1
|
+
{"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../../../../../src/components/ui/Confirm/styles.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;6CAE6C;AAC7C,eAAO,MAAM,UAAU,GAAI,GAAG,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2CrC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Animated,
|
|
4
|
+
Easing,
|
|
5
|
+
Modal,
|
|
6
|
+
Pressable,
|
|
7
|
+
Text,
|
|
8
|
+
View,
|
|
9
|
+
useWindowDimensions,
|
|
10
|
+
} from 'react-native';
|
|
3
11
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
4
12
|
import { space, useThemedStyles } from '../../../theme';
|
|
5
13
|
import { Button } from '../Button';
|
|
@@ -7,6 +15,10 @@ import { _subs } from './confirm';
|
|
|
7
15
|
import { makeStyles } from './styles';
|
|
8
16
|
import type { ConfirmEntry, Subscriber } from './types';
|
|
9
17
|
|
|
18
|
+
/** 遮罩淡入 / 淡出时长(ms)—— 出场略长于入场,与 Modal slide 退场(~300ms)大致同步收完。 */
|
|
19
|
+
const SCRIM_IN_MS = 220;
|
|
20
|
+
const SCRIM_OUT_MS = 260;
|
|
21
|
+
|
|
10
22
|
/**
|
|
11
23
|
* Confirm 对话框宿主 —— App 根挂一次,监听 `confirm()` 调用并渲染。
|
|
12
24
|
*
|
|
@@ -17,6 +29,7 @@ import type { ConfirmEntry, Subscriber } from './types';
|
|
|
17
29
|
export function ConfirmHost(): React.JSX.Element | null {
|
|
18
30
|
const styles = useThemedStyles(makeStyles);
|
|
19
31
|
const insets = useSafeAreaInsets();
|
|
32
|
+
const { height: screenH } = useWindowDimensions();
|
|
20
33
|
const [entry, setEntry] = useState<ConfirmEntry | null>(null);
|
|
21
34
|
|
|
22
35
|
// 跟踪当前未决 entry,供 unmount cleanup resolve(false)。
|
|
@@ -51,6 +64,18 @@ export function ConfirmHost(): React.JSX.Element | null {
|
|
|
51
64
|
setEntry(null);
|
|
52
65
|
}, [entry]);
|
|
53
66
|
|
|
67
|
+
// 遮罩自持淡入 / 淡出(不蹭 Modal 的位移动画,理由见下方 scrim 注释)。退场也要淡:
|
|
68
|
+
// entry 置 null 后 Modal 仍在滑出、本层仍挂载,不淡的话遮罩会在滑出末尾硬切消失。
|
|
69
|
+
const scrimOpacity = useRef(new Animated.Value(0)).current;
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
Animated.timing(scrimOpacity, {
|
|
72
|
+
toValue: entry ? 1 : 0,
|
|
73
|
+
duration: entry ? SCRIM_IN_MS : SCRIM_OUT_MS,
|
|
74
|
+
easing: Easing.out(Easing.quad),
|
|
75
|
+
useNativeDriver: true,
|
|
76
|
+
}).start();
|
|
77
|
+
}, [entry, scrimOpacity]);
|
|
78
|
+
|
|
54
79
|
return (
|
|
55
80
|
<Modal
|
|
56
81
|
visible={!!entry}
|
|
@@ -67,6 +92,18 @@ export function ConfirmHost(): React.JSX.Element | null {
|
|
|
67
92
|
// 遮蔽 sheet 内的 title/message/按钮。SR 取消路径走 cancel 按钮 + onRequestClose。
|
|
68
93
|
accessible={false}
|
|
69
94
|
>
|
|
95
|
+
{/* 半透黑遮罩 —— 两点都是必需的,少一点就露白:
|
|
96
|
+
① **向上外扩一屏**:animationType="slide" 位移的是 Modal 的**整个容器**(遮罩也在其中),
|
|
97
|
+
位移 t 时屏幕 [0, t) 这段根本没有遮罩 —— 顶部挂着一条未压暗的白带,一路缩到动画
|
|
98
|
+
结束才没(缓出末段最慢,细白带在状态栏那儿赖得最久)。外扩后任意 t 都盖满视口。
|
|
99
|
+
② **自持淡入淡出**:外扩解决了「盖不到」,但遮罩会随容器一起硬切出现 —— 故不蹭容器的
|
|
100
|
+
位移动画,自己走 opacity。sheet 的 slide 仍由 Modal 原生驱动。
|
|
101
|
+
pointerEvents=none:点击穿透到 backdrop(取消路径不变)。 */}
|
|
102
|
+
<Animated.View
|
|
103
|
+
pointerEvents="none"
|
|
104
|
+
style={[styles.scrim, { top: -screenH, opacity: scrimOpacity }]}
|
|
105
|
+
testID="confirm-scrim"
|
|
106
|
+
/>
|
|
70
107
|
<Pressable
|
|
71
108
|
style={[styles.sheet, { paddingBottom: insets.bottom + space['7'] }]}
|
|
72
109
|
onPress={() => {}}
|
|
@@ -3,14 +3,24 @@ import { fw, r, space, type as t } from '../../../theme';
|
|
|
3
3
|
import type { ColorTokens } from '../../../theme';
|
|
4
4
|
|
|
5
5
|
/** Confirm 对话框 styles —— 原生 Modal(transparent + slide)底部弹出卡片:
|
|
6
|
-
* backdrop
|
|
6
|
+
* backdrop 铺满、点击取消(**不挂半透黑** —— 见 ConfirmHost 的 scrim 注释);
|
|
7
|
+
* scrim 半透黑独立一层;sheet 贴底圆角顶。不依赖 @gorhom。 */
|
|
7
8
|
export const makeStyles = (c: ColorTokens) =>
|
|
8
9
|
StyleSheet.create({
|
|
9
10
|
backdrop: {
|
|
10
11
|
flex: 1,
|
|
11
|
-
backgroundColor: c.scrim,
|
|
12
12
|
justifyContent: 'flex-end',
|
|
13
13
|
},
|
|
14
|
+
// 半透黑遮罩层(纯视觉,不接触控):绝对定位四边贴合 backdrop,`top` 由 ConfirmHost 内联
|
|
15
|
+
// 覆写成 -屏高(向上外扩一屏,盖住 Modal slide 位移期露出的顶部)。
|
|
16
|
+
scrim: {
|
|
17
|
+
position: 'absolute',
|
|
18
|
+
top: 0,
|
|
19
|
+
left: 0,
|
|
20
|
+
right: 0,
|
|
21
|
+
bottom: 0,
|
|
22
|
+
backgroundColor: c.scrim,
|
|
23
|
+
},
|
|
14
24
|
sheet: {
|
|
15
25
|
backgroundColor: c.surface,
|
|
16
26
|
borderTopLeftRadius: r(20),
|