@particle-network/ui-native 0.0.2
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/README.md +57 -0
- package/dist/components/ProgressWrapper/index.d.ts +35 -0
- package/dist/components/ProgressWrapper/index.js +120 -0
- package/dist/components/Text/index.d.ts +2 -0
- package/dist/components/Text/index.js +2 -0
- package/dist/components/Text/text.d.ts +4 -0
- package/dist/components/Text/text.js +196 -0
- package/dist/components/Text/text.types.d.ts +109 -0
- package/dist/components/Text/text.types.js +77 -0
- package/dist/components/UXButton/button.d.ts +3 -0
- package/dist/components/UXButton/button.js +74 -0
- package/dist/components/UXButton/button.styles.d.ts +53 -0
- package/dist/components/UXButton/button.styles.js +157 -0
- package/dist/components/UXButton/button.types.d.ts +18 -0
- package/dist/components/UXButton/button.types.js +0 -0
- package/dist/components/UXButton/index.d.ts +2 -0
- package/dist/components/UXButton/index.js +1 -0
- package/dist/components/UXCheckbox/checkbox-group.d.ts +8 -0
- package/dist/components/UXCheckbox/checkbox-group.js +30 -0
- package/dist/components/UXCheckbox/checkbox.d.ts +10 -0
- package/dist/components/UXCheckbox/checkbox.js +78 -0
- package/dist/components/UXCheckbox/context.d.ts +8 -0
- package/dist/components/UXCheckbox/context.js +8 -0
- package/dist/components/UXCheckbox/index.d.ts +4 -0
- package/dist/components/UXCheckbox/index.js +3 -0
- package/dist/components/UXCheckbox/types.d.ts +6 -0
- package/dist/components/UXCheckbox/types.js +0 -0
- package/dist/components/UXChip/chip.d.ts +3 -0
- package/dist/components/UXChip/chip.js +48 -0
- package/dist/components/UXChip/index.d.ts +2 -0
- package/dist/components/UXChip/index.js +1 -0
- package/dist/components/UXChip/styles.d.ts +24 -0
- package/dist/components/UXChip/styles.js +70 -0
- package/dist/components/UXChip/types.d.ts +11 -0
- package/dist/components/UXChip/types.js +0 -0
- package/dist/components/UXDivider/index.d.ts +8 -0
- package/dist/components/UXDivider/index.js +34 -0
- package/dist/components/UXHint/index.d.ts +7 -0
- package/dist/components/UXHint/index.js +19 -0
- package/dist/components/UXModal/index.d.ts +24 -0
- package/dist/components/UXModal/index.js +271 -0
- package/dist/components/UXPressable/index.d.ts +4 -0
- package/dist/components/UXPressable/index.js +36 -0
- package/dist/components/UXRadio/context.d.ts +7 -0
- package/dist/components/UXRadio/context.js +10 -0
- package/dist/components/UXRadio/index.d.ts +2 -0
- package/dist/components/UXRadio/index.js +2 -0
- package/dist/components/UXRadio/radio-group.d.ts +5 -0
- package/dist/components/UXRadio/radio-group.js +21 -0
- package/dist/components/UXRadio/radio.d.ts +5 -0
- package/dist/components/UXRadio/radio.js +67 -0
- package/dist/components/UXRadio/types.d.ts +10 -0
- package/dist/components/UXRadio/types.js +0 -0
- package/dist/components/UXSwitch/index.d.ts +2 -0
- package/dist/components/UXSwitch/index.js +1 -0
- package/dist/components/UXSwitch/styles.d.ts +16 -0
- package/dist/components/UXSwitch/styles.js +58 -0
- package/dist/components/UXSwitch/switch.d.ts +3 -0
- package/dist/components/UXSwitch/switch.js +103 -0
- package/dist/components/UXSwitch/types.d.ts +11 -0
- package/dist/components/UXSwitch/types.js +0 -0
- package/dist/components/UXTabs/context.d.ts +5 -0
- package/dist/components/UXTabs/context.js +8 -0
- package/dist/components/UXTabs/index.d.ts +4 -0
- package/dist/components/UXTabs/index.js +3 -0
- package/dist/components/UXTabs/styles.d.ts +30 -0
- package/dist/components/UXTabs/styles.js +191 -0
- package/dist/components/UXTabs/tab.d.ts +3 -0
- package/dist/components/UXTabs/tab.js +55 -0
- package/dist/components/UXTabs/tabs.d.ts +3 -0
- package/dist/components/UXTabs/tabs.js +66 -0
- package/dist/components/UXTabs/types.d.ts +23 -0
- package/dist/components/UXTabs/types.js +0 -0
- package/dist/components/UXTooltip/index.d.ts +6 -0
- package/dist/components/UXTooltip/index.js +32 -0
- package/dist/components/UXTouchableOpacity/index.d.ts +4 -0
- package/dist/components/UXTouchableOpacity/index.js +24 -0
- package/dist/components/index.d.ts +22 -0
- package/dist/components/index.js +22 -0
- package/dist/components/input/index.d.ts +3 -0
- package/dist/components/input/index.js +2 -0
- package/dist/components/input/input.d.ts +3 -0
- package/dist/components/input/input.js +138 -0
- package/dist/components/input/number-input.d.ts +3 -0
- package/dist/components/input/number-input.js +231 -0
- package/dist/components/input/styles.d.ts +31 -0
- package/dist/components/input/styles.js +102 -0
- package/dist/components/input/types.d.ts +61 -0
- package/dist/components/input/types.js +0 -0
- package/dist/components/layout/Box/box.d.ts +12 -0
- package/dist/components/layout/Box/box.js +89 -0
- package/dist/components/layout/Box/index.d.ts +3 -0
- package/dist/components/layout/Box/index.js +2 -0
- package/dist/components/layout/Box/useBox.style.d.ts +3 -0
- package/dist/components/layout/Box/useBox.style.js +141 -0
- package/dist/components/layout/Box/useBox.type.d.ts +292 -0
- package/dist/components/layout/Box/useBox.type.js +0 -0
- package/dist/components/layout/Center.d.ts +5 -0
- package/dist/components/layout/Center.js +10 -0
- package/dist/components/layout/Circle.d.ts +7 -0
- package/dist/components/layout/Circle.js +14 -0
- package/dist/components/layout/Flex/flex.d.ts +6 -0
- package/dist/components/layout/Flex/flex.js +33 -0
- package/dist/components/layout/Flex/index.d.ts +4 -0
- package/dist/components/layout/Flex/index.js +3 -0
- package/dist/components/layout/Flex/useFlex.style.d.ts +3 -0
- package/dist/components/layout/Flex/useFlex.style.js +122 -0
- package/dist/components/layout/Flex/useFlex.type.d.ts +65 -0
- package/dist/components/layout/Flex/useFlex.type.js +0 -0
- package/dist/components/layout/Flex/useFlexBox.style.d.ts +134 -0
- package/dist/components/layout/Flex/useFlexBox.style.js +26 -0
- package/dist/components/layout/HStack.d.ts +5 -0
- package/dist/components/layout/HStack.js +11 -0
- package/dist/components/layout/Square.d.ts +9 -0
- package/dist/components/layout/Square.js +14 -0
- package/dist/components/layout/VStack.d.ts +5 -0
- package/dist/components/layout/VStack.js +14 -0
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/useColors.d.ts +7 -0
- package/dist/hooks/useColors.js +18 -0
- package/dist/hooks/useKeyboard.d.ts +4 -0
- package/dist/hooks/useKeyboard.js +29 -0
- package/dist/hooks/useRadius.d.ts +7 -0
- package/dist/hooks/useRadius.js +16 -0
- package/dist/hooks/useSpacing.d.ts +7 -0
- package/dist/hooks/useSpacing.js +16 -0
- package/dist/hooks/useTheme.d.ts +5 -0
- package/dist/hooks/useTheme.js +6 -0
- package/dist/icons/CheckboxOffIcon.d.ts +4 -0
- package/dist/icons/CheckboxOffIcon.js +21 -0
- package/dist/icons/CheckboxOnIcon.d.ts +4 -0
- package/dist/icons/CheckboxOnIcon.js +26 -0
- package/dist/icons/DotIcon.d.ts +4 -0
- package/dist/icons/DotIcon.js +23 -0
- package/dist/icons/QuestionIcon.d.ts +4 -0
- package/dist/icons/QuestionIcon.js +30 -0
- package/dist/icons/RadioOffIcon.d.ts +4 -0
- package/dist/icons/RadioOffIcon.js +22 -0
- package/dist/icons/RadioOnIcon.d.ts +4 -0
- package/dist/icons/RadioOnIcon.js +42 -0
- package/dist/icons/types.d.ts +6 -0
- package/dist/icons/types.js +0 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +4 -0
- package/dist/provider/ThemeContext.d.ts +3 -0
- package/dist/provider/ThemeContext.js +15 -0
- package/dist/provider/ThemeProvider.d.ts +10 -0
- package/dist/provider/ThemeProvider.js +31 -0
- package/dist/provider/index.d.ts +2 -0
- package/dist/provider/index.js +2 -0
- package/dist/theme/colors.d.ts +6 -0
- package/dist/theme/colors.js +17 -0
- package/dist/theme/index.d.ts +7 -0
- package/dist/theme/index.js +16 -0
- package/dist/theme/opacity.d.ts +2 -0
- package/dist/theme/opacity.js +3 -0
- package/dist/theme/radius.d.ts +3 -0
- package/dist/theme/radius.js +10 -0
- package/dist/theme/spacing.d.ts +3 -0
- package/dist/theme/spacing.js +10 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +0 -0
- package/dist/types/theme.d.ts +17 -0
- package/dist/types/theme.js +0 -0
- package/dist/utils/triggerHapticFeedback.d.ts +5 -0
- package/dist/utils/triggerHapticFeedback.js +16 -0
- package/package.json +110 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ScrollView, type StyleProp, type ViewStyle } from 'react-native';
|
|
3
|
+
export interface UXModalProps {
|
|
4
|
+
modalName?: string;
|
|
5
|
+
style?: StyleProp<ViewStyle>;
|
|
6
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
title?: React.ReactNode;
|
|
9
|
+
titleAlign?: 'left' | 'center';
|
|
10
|
+
footer?: React.ReactNode;
|
|
11
|
+
tip?: React.ReactNode;
|
|
12
|
+
onClose?: () => void;
|
|
13
|
+
onVisibleChange?: (visible: boolean) => void;
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
disableCloseBySwipe?: boolean;
|
|
16
|
+
closeByLineOnly?: boolean;
|
|
17
|
+
wrapPortal?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* 键盘避开区域
|
|
20
|
+
* 如果想要遮挡 footer 则选择 content
|
|
21
|
+
*/
|
|
22
|
+
keyboardAvoidPosition?: 'container' | 'content';
|
|
23
|
+
}
|
|
24
|
+
export declare const UXModal: React.ForwardRefExoticComponent<UXModalProps & React.RefAttributes<ScrollView>>;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import react, { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { Animated, Dimensions, PanResponder, Platform, ScrollView, StyleSheet } from "react-native";
|
|
4
|
+
import { Modal, Portal } from "react-native-paper";
|
|
5
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
6
|
+
import { ms } from "react-native-size-matters";
|
|
7
|
+
import { Box, Circle, Flex, HStack, Text, VStack } from "../index.js";
|
|
8
|
+
import { useColors, useKeyboard } from "../../hooks/index.js";
|
|
9
|
+
const { height } = Dimensions.get('window');
|
|
10
|
+
const UXModal = /*#__PURE__*/ forwardRef((props, scrollViewRef)=>{
|
|
11
|
+
const { style, contentStyle, isOpen, title, titleAlign = 'left', onClose, onVisibleChange, children, disableCloseBySwipe, closeByLineOnly, wrapPortal, footer, modalName, tip, keyboardAvoidPosition = 'container' } = props;
|
|
12
|
+
const { getColor } = useColors();
|
|
13
|
+
const insets = useSafeAreaInsets();
|
|
14
|
+
const { keyboardHeight } = useKeyboard();
|
|
15
|
+
const translateY = useRef(new Animated.Value(height)).current;
|
|
16
|
+
const scrollOffset = useRef(0);
|
|
17
|
+
const [touchStartY, setTouchStartY] = useState(0);
|
|
18
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
19
|
+
const [modalHeight, setModalHeight] = useState(0);
|
|
20
|
+
const [footerHeight, setFooterHeight] = useState(0);
|
|
21
|
+
const isAndroid = 'android' === Platform.OS;
|
|
22
|
+
const onDismiss = useCallback(()=>{
|
|
23
|
+
if (onClose) onClose();
|
|
24
|
+
else if (onVisibleChange) onVisibleChange(false);
|
|
25
|
+
translateY.setValue(height);
|
|
26
|
+
}, [
|
|
27
|
+
onClose,
|
|
28
|
+
onVisibleChange
|
|
29
|
+
]);
|
|
30
|
+
useEffect(()=>{
|
|
31
|
+
if (isOpen && modalName) console.log('BottomModal:', modalName);
|
|
32
|
+
}, [
|
|
33
|
+
isOpen,
|
|
34
|
+
modalName
|
|
35
|
+
]);
|
|
36
|
+
useEffect(()=>{
|
|
37
|
+
if (isOpen) Animated.spring(translateY, {
|
|
38
|
+
toValue: 0,
|
|
39
|
+
useNativeDriver: !isAndroid,
|
|
40
|
+
damping: 500,
|
|
41
|
+
mass: 3,
|
|
42
|
+
stiffness: 1000
|
|
43
|
+
}).start();
|
|
44
|
+
else Animated.timing(translateY, {
|
|
45
|
+
toValue: height,
|
|
46
|
+
duration: 200,
|
|
47
|
+
useNativeDriver: !isAndroid
|
|
48
|
+
}).start();
|
|
49
|
+
}, [
|
|
50
|
+
isOpen,
|
|
51
|
+
height,
|
|
52
|
+
translateY
|
|
53
|
+
]);
|
|
54
|
+
const panResponder = PanResponder.create({
|
|
55
|
+
onStartShouldSetPanResponder: ()=>false,
|
|
56
|
+
onMoveShouldSetPanResponder: (_, gestureState)=>Math.abs(gestureState.dy) > Math.abs(gestureState.dx) && scrollOffset.current <= 0 && gestureState.dy > 0,
|
|
57
|
+
onPanResponderMove: (_, gestureState)=>{
|
|
58
|
+
if (gestureState.dy > 0) translateY.setValue(gestureState.dy);
|
|
59
|
+
},
|
|
60
|
+
onPanResponderRelease: (_, gestureState)=>{
|
|
61
|
+
if (gestureState.dy > 100) Animated.timing(translateY, {
|
|
62
|
+
toValue: height,
|
|
63
|
+
duration: 200,
|
|
64
|
+
useNativeDriver: !isAndroid
|
|
65
|
+
}).start(()=>{
|
|
66
|
+
onDismiss();
|
|
67
|
+
});
|
|
68
|
+
else Animated.spring(translateY, {
|
|
69
|
+
toValue: 0,
|
|
70
|
+
useNativeDriver: !isAndroid
|
|
71
|
+
}).start();
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
const handleScroll = useCallback((event)=>{
|
|
75
|
+
scrollOffset.current = event.nativeEvent.contentOffset.y;
|
|
76
|
+
}, []);
|
|
77
|
+
const onTouchStartSideLine = useCallback((event)=>{
|
|
78
|
+
setTouchStartY(event.nativeEvent.pageY);
|
|
79
|
+
setIsDragging(true);
|
|
80
|
+
}, []);
|
|
81
|
+
const onTouchMoveSideLine = useCallback((event)=>{
|
|
82
|
+
if (!isDragging) return;
|
|
83
|
+
const currentY = event.nativeEvent.pageY;
|
|
84
|
+
const deltaY = currentY - touchStartY;
|
|
85
|
+
if (deltaY > 0) translateY.setValue(deltaY);
|
|
86
|
+
}, [
|
|
87
|
+
isDragging,
|
|
88
|
+
touchStartY,
|
|
89
|
+
translateY
|
|
90
|
+
]);
|
|
91
|
+
const onTouchEndSideLine = useCallback((event)=>{
|
|
92
|
+
if (!isDragging) return;
|
|
93
|
+
const currentY = event.nativeEvent.pageY;
|
|
94
|
+
const deltaY = currentY - touchStartY;
|
|
95
|
+
const threshold = 0.1 * modalHeight;
|
|
96
|
+
if (deltaY > threshold) Animated.timing(translateY, {
|
|
97
|
+
toValue: height,
|
|
98
|
+
duration: 200,
|
|
99
|
+
useNativeDriver: !isAndroid
|
|
100
|
+
}).start(()=>{
|
|
101
|
+
onDismiss();
|
|
102
|
+
});
|
|
103
|
+
else Animated.spring(translateY, {
|
|
104
|
+
toValue: 0,
|
|
105
|
+
useNativeDriver: !isAndroid
|
|
106
|
+
}).start();
|
|
107
|
+
setIsDragging(false);
|
|
108
|
+
}, [
|
|
109
|
+
isDragging,
|
|
110
|
+
touchStartY,
|
|
111
|
+
modalHeight,
|
|
112
|
+
height,
|
|
113
|
+
translateY,
|
|
114
|
+
onDismiss
|
|
115
|
+
]);
|
|
116
|
+
const keyboardAvoidValue = useMemo(()=>{
|
|
117
|
+
const containerValue = 'container' === keyboardAvoidPosition ? Math.max(insets.bottom, ms(32), keyboardHeight) : Math.max(insets.bottom, ms(32));
|
|
118
|
+
const contentValue = 'content' === keyboardAvoidPosition && keyboardHeight > 0 ? keyboardHeight - footerHeight - Math.max(insets.bottom, ms(32)) : 0;
|
|
119
|
+
return {
|
|
120
|
+
containerValue,
|
|
121
|
+
contentValue
|
|
122
|
+
};
|
|
123
|
+
}, [
|
|
124
|
+
keyboardAvoidPosition,
|
|
125
|
+
insets.bottom,
|
|
126
|
+
keyboardHeight,
|
|
127
|
+
footerHeight
|
|
128
|
+
]);
|
|
129
|
+
const styles = StyleSheet.create({
|
|
130
|
+
modalContainer: {
|
|
131
|
+
margin: 0,
|
|
132
|
+
justifyContent: 'flex-end',
|
|
133
|
+
position: 'absolute',
|
|
134
|
+
bottom: 0,
|
|
135
|
+
left: 0,
|
|
136
|
+
right: 0
|
|
137
|
+
},
|
|
138
|
+
modal: {
|
|
139
|
+
marginBottom: 0,
|
|
140
|
+
marginTop: 0,
|
|
141
|
+
backgroundColor: 'transparent'
|
|
142
|
+
},
|
|
143
|
+
container: {
|
|
144
|
+
paddingHorizontal: ms(14),
|
|
145
|
+
borderTopLeftRadius: 24,
|
|
146
|
+
borderTopRightRadius: 24,
|
|
147
|
+
flex: 1,
|
|
148
|
+
gap: ms(20),
|
|
149
|
+
backgroundColor: getColor('overlay'),
|
|
150
|
+
maxHeight: isAndroid ? height : height - insets.top,
|
|
151
|
+
transform: [
|
|
152
|
+
{
|
|
153
|
+
translateY
|
|
154
|
+
}
|
|
155
|
+
],
|
|
156
|
+
paddingBottom: keyboardAvoidValue.containerValue
|
|
157
|
+
},
|
|
158
|
+
content: {
|
|
159
|
+
marginBottom: keyboardAvoidValue.contentValue
|
|
160
|
+
},
|
|
161
|
+
tip: {
|
|
162
|
+
flex: 1,
|
|
163
|
+
flexShrink: 1,
|
|
164
|
+
flexWrap: 'wrap'
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
const Wrapper = useMemo(()=>wrapPortal ? Portal : react.Fragment, [
|
|
168
|
+
wrapPortal
|
|
169
|
+
]);
|
|
170
|
+
return /*#__PURE__*/ jsx(Wrapper, {
|
|
171
|
+
children: /*#__PURE__*/ jsx(Modal, {
|
|
172
|
+
dismissable: true,
|
|
173
|
+
dismissableBackButton: true,
|
|
174
|
+
contentContainerStyle: styles.modalContainer,
|
|
175
|
+
style: [
|
|
176
|
+
styles.modal,
|
|
177
|
+
style
|
|
178
|
+
],
|
|
179
|
+
theme: {
|
|
180
|
+
colors: {
|
|
181
|
+
backdrop: 'rgba(0, 0, 0, 0.8)'
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
visible: isOpen,
|
|
185
|
+
onDismiss: onDismiss,
|
|
186
|
+
children: /*#__PURE__*/ jsxs(Animated.View, {
|
|
187
|
+
style: styles.container,
|
|
188
|
+
onLayout: (event)=>{
|
|
189
|
+
setModalHeight(event.nativeEvent.layout.height);
|
|
190
|
+
},
|
|
191
|
+
...!disableCloseBySwipe && !closeByLineOnly ? panResponder.panHandlers : {},
|
|
192
|
+
children: [
|
|
193
|
+
/*#__PURE__*/ jsx(HStack, {
|
|
194
|
+
fullWidth: true,
|
|
195
|
+
hitSlop: {
|
|
196
|
+
top: 20,
|
|
197
|
+
bottom: 20,
|
|
198
|
+
left: 0,
|
|
199
|
+
right: 0
|
|
200
|
+
},
|
|
201
|
+
justify: "center",
|
|
202
|
+
mt: 10,
|
|
203
|
+
onTouchEnd: onTouchEndSideLine,
|
|
204
|
+
onTouchMove: onTouchMoveSideLine,
|
|
205
|
+
onTouchStart: onTouchStartSideLine,
|
|
206
|
+
children: /*#__PURE__*/ jsx(Box, {
|
|
207
|
+
bg: "tertiary",
|
|
208
|
+
h: 4,
|
|
209
|
+
radius: "full",
|
|
210
|
+
w: 40
|
|
211
|
+
})
|
|
212
|
+
}),
|
|
213
|
+
title ? /*#__PURE__*/ jsx(Flex, {
|
|
214
|
+
fullWidth: true,
|
|
215
|
+
justify: 'center' === titleAlign ? 'center' : 'start',
|
|
216
|
+
mt: -4,
|
|
217
|
+
children: 'string' == typeof title ? /*#__PURE__*/ jsx(Text, {
|
|
218
|
+
h3: true,
|
|
219
|
+
children: title
|
|
220
|
+
}) : title
|
|
221
|
+
}) : null,
|
|
222
|
+
/*#__PURE__*/ jsx(ScrollView, {
|
|
223
|
+
ref: scrollViewRef,
|
|
224
|
+
bounces: false,
|
|
225
|
+
scrollEventThrottle: 16,
|
|
226
|
+
showsHorizontalScrollIndicator: false,
|
|
227
|
+
showsVerticalScrollIndicator: false,
|
|
228
|
+
style: [
|
|
229
|
+
styles.content,
|
|
230
|
+
contentStyle
|
|
231
|
+
],
|
|
232
|
+
onScroll: handleScroll,
|
|
233
|
+
children: children
|
|
234
|
+
}),
|
|
235
|
+
/*#__PURE__*/ jsxs(VStack, {
|
|
236
|
+
fullWidth: true,
|
|
237
|
+
gap: "lg",
|
|
238
|
+
onLayout: (event)=>{
|
|
239
|
+
setFooterHeight(event.nativeEvent.layout.height);
|
|
240
|
+
},
|
|
241
|
+
children: [
|
|
242
|
+
footer ? /*#__PURE__*/ jsx(VStack, {
|
|
243
|
+
fullWidth: true,
|
|
244
|
+
gap: "lg",
|
|
245
|
+
children: footer
|
|
246
|
+
}) : null,
|
|
247
|
+
tip ? /*#__PURE__*/ jsxs(Flex, {
|
|
248
|
+
gap: 8,
|
|
249
|
+
children: [
|
|
250
|
+
/*#__PURE__*/ jsx(Circle, {
|
|
251
|
+
bg: "secondary",
|
|
252
|
+
mt: 5,
|
|
253
|
+
size: 5
|
|
254
|
+
}),
|
|
255
|
+
/*#__PURE__*/ jsx(Text, {
|
|
256
|
+
body2: true,
|
|
257
|
+
color: "secondary",
|
|
258
|
+
style: styles.tip,
|
|
259
|
+
children: tip
|
|
260
|
+
})
|
|
261
|
+
]
|
|
262
|
+
}) : null
|
|
263
|
+
]
|
|
264
|
+
})
|
|
265
|
+
]
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
UXModal.displayName = 'UXModal';
|
|
271
|
+
export { UXModal };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { PressableProps, View } from 'react-native';
|
|
2
|
+
import { type UseBoxProps, type UseFlexProps } from '..';
|
|
3
|
+
export type UXPressableProps = PressableProps & UseBoxProps & UseFlexProps;
|
|
4
|
+
export declare const UXPressable: import("react").ForwardRefExoticComponent<PressableProps & UseBoxProps & UseFlexProps & import("react").RefAttributes<View>>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useCallback } from "react";
|
|
3
|
+
import { Pressable } from "react-native";
|
|
4
|
+
import useDebounceFn from "ahooks/es/useDebounceFn";
|
|
5
|
+
import { useFlexBoxStyle } from "../index.js";
|
|
6
|
+
const UXPressable = /*#__PURE__*/ forwardRef(({ style, onPress, ...props }, ref)=>{
|
|
7
|
+
const { run } = useDebounceFn(onPress || (()=>null), {
|
|
8
|
+
wait: 300,
|
|
9
|
+
leading: true,
|
|
10
|
+
trailing: false
|
|
11
|
+
});
|
|
12
|
+
const boxFlexStyle = useFlexBoxStyle(props);
|
|
13
|
+
const pressableStyle = useCallback(({ pressed })=>{
|
|
14
|
+
if ('function' == typeof style) return [
|
|
15
|
+
boxFlexStyle,
|
|
16
|
+
style({
|
|
17
|
+
pressed
|
|
18
|
+
})
|
|
19
|
+
];
|
|
20
|
+
return [
|
|
21
|
+
boxFlexStyle,
|
|
22
|
+
style
|
|
23
|
+
];
|
|
24
|
+
}, [
|
|
25
|
+
boxFlexStyle,
|
|
26
|
+
style
|
|
27
|
+
]);
|
|
28
|
+
return /*#__PURE__*/ jsx(Pressable, {
|
|
29
|
+
ref: ref,
|
|
30
|
+
style: pressableStyle,
|
|
31
|
+
onPress: run,
|
|
32
|
+
...props
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
UXPressable.displayName = 'UXPressable';
|
|
36
|
+
export { UXPressable };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { UXRadioCommonProps } from './types';
|
|
2
|
+
type RadioGroupContextType = Omit<UXRadioCommonProps, 'children' | 'value'> & {
|
|
3
|
+
selectedValue: UXRadioCommonProps['value'];
|
|
4
|
+
};
|
|
5
|
+
export declare const RadioGroupContext: import("react").Context<RadioGroupContextType>;
|
|
6
|
+
export declare const useRadioGroup: () => RadioGroupContextType;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
const RadioGroupContext = createContext({
|
|
3
|
+
selectedValue: '',
|
|
4
|
+
onValueChange: ()=>null
|
|
5
|
+
});
|
|
6
|
+
const useRadioGroup = ()=>{
|
|
7
|
+
const context = useContext(RadioGroupContext);
|
|
8
|
+
return context;
|
|
9
|
+
};
|
|
10
|
+
export { RadioGroupContext, useRadioGroup };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import "react";
|
|
3
|
+
import { Flex } from "../index.js";
|
|
4
|
+
import { RadioGroupContext } from "./context.js";
|
|
5
|
+
const UXRadioGroup = ({ value = '', onValueChange, color, size, isDisabled, children, gap = 8, ...props })=>/*#__PURE__*/ jsx(RadioGroupContext.Provider, {
|
|
6
|
+
value: {
|
|
7
|
+
size,
|
|
8
|
+
isDisabled,
|
|
9
|
+
color,
|
|
10
|
+
selectedValue: value,
|
|
11
|
+
onValueChange
|
|
12
|
+
},
|
|
13
|
+
children: /*#__PURE__*/ jsx(Flex, {
|
|
14
|
+
direction: "col",
|
|
15
|
+
gap: gap,
|
|
16
|
+
...props,
|
|
17
|
+
children: children
|
|
18
|
+
})
|
|
19
|
+
});
|
|
20
|
+
UXRadioGroup.displayName = 'UXRadioGroup';
|
|
21
|
+
export { UXRadioGroup };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo } from "react";
|
|
3
|
+
import { Pressable } from "react-native";
|
|
4
|
+
import { ms } from "react-native-size-matters";
|
|
5
|
+
import { Flex, Text } from "../index.js";
|
|
6
|
+
import RadioOffIcon from "../../icons/RadioOffIcon.js";
|
|
7
|
+
import RadioOnIcon from "../../icons/RadioOnIcon.js";
|
|
8
|
+
import { disabledOpacity } from "../../theme/index.js";
|
|
9
|
+
import { useRadioGroup } from "./context.js";
|
|
10
|
+
const UXRadio = ({ size, color, children, value, isDisabled, ...props })=>{
|
|
11
|
+
const groupContext = useRadioGroup();
|
|
12
|
+
const { selectedValue, size: groupSize, color: groupColor, onValueChange, isDisabled: groupIsDisabled } = groupContext;
|
|
13
|
+
const isSelected = selectedValue === value;
|
|
14
|
+
const radioColor = color || groupColor || 'primary';
|
|
15
|
+
const radioSize = size || groupSize || 'md';
|
|
16
|
+
const radioIsDisabled = isDisabled || groupIsDisabled;
|
|
17
|
+
const handleSelectChange = ()=>{
|
|
18
|
+
onValueChange(value);
|
|
19
|
+
};
|
|
20
|
+
const iconSize = useMemo(()=>{
|
|
21
|
+
if ('sm' === radioSize) return ms(10);
|
|
22
|
+
if ('lg' === radioSize) return ms(14);
|
|
23
|
+
return ms(12);
|
|
24
|
+
}, [
|
|
25
|
+
radioSize
|
|
26
|
+
]);
|
|
27
|
+
const labelVariant = useMemo(()=>{
|
|
28
|
+
if ('sm' === radioSize) return 'body3';
|
|
29
|
+
if ('lg' === radioSize) return 'body1';
|
|
30
|
+
return 'body2';
|
|
31
|
+
}, [
|
|
32
|
+
radioSize
|
|
33
|
+
]);
|
|
34
|
+
const gap = useMemo(()=>{
|
|
35
|
+
if ('sm' === radioSize) return 6;
|
|
36
|
+
return 8;
|
|
37
|
+
}, [
|
|
38
|
+
radioSize
|
|
39
|
+
]);
|
|
40
|
+
return /*#__PURE__*/ jsx(Pressable, {
|
|
41
|
+
disabled: radioIsDisabled,
|
|
42
|
+
style: {
|
|
43
|
+
opacity: radioIsDisabled ? disabledOpacity : 1
|
|
44
|
+
},
|
|
45
|
+
onPress: handleSelectChange,
|
|
46
|
+
children: /*#__PURE__*/ jsxs(Flex, {
|
|
47
|
+
gap: gap,
|
|
48
|
+
items: "center",
|
|
49
|
+
...props,
|
|
50
|
+
children: [
|
|
51
|
+
isSelected ? /*#__PURE__*/ jsx(RadioOnIcon, {
|
|
52
|
+
color: radioColor,
|
|
53
|
+
size: iconSize
|
|
54
|
+
}) : /*#__PURE__*/ jsx(RadioOffIcon, {
|
|
55
|
+
color: "secondary",
|
|
56
|
+
size: iconSize
|
|
57
|
+
}),
|
|
58
|
+
'string' == typeof children ? /*#__PURE__*/ jsx(Text, {
|
|
59
|
+
variant: labelVariant,
|
|
60
|
+
children: children
|
|
61
|
+
}) : children
|
|
62
|
+
]
|
|
63
|
+
})
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
UXRadio.displayName = 'UXRadio';
|
|
67
|
+
export { UXRadio };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type React from 'react';
|
|
2
|
+
import type { UXForegroundColor } from '@particle-network/ui-shared';
|
|
3
|
+
export interface UXRadioCommonProps {
|
|
4
|
+
color?: UXForegroundColor;
|
|
5
|
+
size?: 'sm' | 'md' | 'lg';
|
|
6
|
+
isDisabled?: boolean;
|
|
7
|
+
value: string;
|
|
8
|
+
onValueChange: (value: string) => void;
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./switch.js";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { UXSwitchProps } from './types';
|
|
2
|
+
export declare const useStyles: ({ size, isSelected }: UXSwitchProps) => {
|
|
3
|
+
track: {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
borderRadius: number;
|
|
7
|
+
justifyContent: "center";
|
|
8
|
+
};
|
|
9
|
+
thumb: {
|
|
10
|
+
backgroundColor: string;
|
|
11
|
+
width: number;
|
|
12
|
+
height: number;
|
|
13
|
+
borderRadius: number;
|
|
14
|
+
elevation: number;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
2
|
+
import { Animated, StyleSheet } from "react-native";
|
|
3
|
+
import { ms } from "react-native-size-matters";
|
|
4
|
+
const useStyles = ({ size = 'md', isSelected = false })=>{
|
|
5
|
+
const animatedValue = useRef(new Animated.Value(isSelected ? 1 : 0)).current;
|
|
6
|
+
useEffect(()=>{
|
|
7
|
+
Animated.timing(animatedValue, {
|
|
8
|
+
toValue: isSelected ? 1 : 0,
|
|
9
|
+
duration: 200,
|
|
10
|
+
useNativeDriver: false
|
|
11
|
+
}).start();
|
|
12
|
+
}, [
|
|
13
|
+
isSelected,
|
|
14
|
+
animatedValue
|
|
15
|
+
]);
|
|
16
|
+
const height = useMemo(()=>{
|
|
17
|
+
if ('sm' === size) return ms(12);
|
|
18
|
+
if ('lg' === size) return ms(20);
|
|
19
|
+
return ms(14);
|
|
20
|
+
}, [
|
|
21
|
+
size
|
|
22
|
+
]);
|
|
23
|
+
const width = useMemo(()=>{
|
|
24
|
+
if ('sm' === size) return ms(20);
|
|
25
|
+
if ('lg' === size) return ms(34);
|
|
26
|
+
return ms(24);
|
|
27
|
+
}, [
|
|
28
|
+
size
|
|
29
|
+
]);
|
|
30
|
+
const thumbSize = useMemo(()=>{
|
|
31
|
+
if ('sm' === size) return ms(8);
|
|
32
|
+
if ('lg' === size) return ms(14);
|
|
33
|
+
return ms(10);
|
|
34
|
+
}, [
|
|
35
|
+
size
|
|
36
|
+
]);
|
|
37
|
+
const styles = useMemo(()=>StyleSheet.create({
|
|
38
|
+
track: {
|
|
39
|
+
width,
|
|
40
|
+
height,
|
|
41
|
+
borderRadius: height / 2,
|
|
42
|
+
justifyContent: 'center'
|
|
43
|
+
},
|
|
44
|
+
thumb: {
|
|
45
|
+
backgroundColor: 'white',
|
|
46
|
+
width: thumbSize,
|
|
47
|
+
height: thumbSize,
|
|
48
|
+
borderRadius: thumbSize / 2,
|
|
49
|
+
elevation: 2
|
|
50
|
+
}
|
|
51
|
+
}), [
|
|
52
|
+
width,
|
|
53
|
+
height,
|
|
54
|
+
thumbSize
|
|
55
|
+
]);
|
|
56
|
+
return styles;
|
|
57
|
+
};
|
|
58
|
+
export { useStyles };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { Animated } from "react-native";
|
|
4
|
+
import { ms } from "react-native-size-matters";
|
|
5
|
+
import useUpdateEffect from "ahooks/es/useUpdateEffect";
|
|
6
|
+
import { Text, UXTouchableOpacity } from "../index.js";
|
|
7
|
+
import { useColors } from "../../hooks/index.js";
|
|
8
|
+
import { disabledOpacity } from "../../theme/index.js";
|
|
9
|
+
import { useStyles } from "./styles.js";
|
|
10
|
+
const UXSwitch = (props)=>{
|
|
11
|
+
const { color = 'primary', size = 'md', defaultSelected = false, isSelected: isSelectedProps = false, onValueChange, isReadOnly, isDisabled, children, ...flexProps } = props;
|
|
12
|
+
const [isSelected, setIsSelected] = useState(isSelectedProps || defaultSelected);
|
|
13
|
+
useUpdateEffect(()=>{
|
|
14
|
+
setIsSelected(isSelectedProps);
|
|
15
|
+
}, [
|
|
16
|
+
isSelectedProps
|
|
17
|
+
]);
|
|
18
|
+
const { getColor } = useColors();
|
|
19
|
+
const styles = useStyles(props);
|
|
20
|
+
const gap = useMemo(()=>{
|
|
21
|
+
if ('sm' === size) return ms(6);
|
|
22
|
+
return ms(8);
|
|
23
|
+
}, [
|
|
24
|
+
size
|
|
25
|
+
]);
|
|
26
|
+
const labelVariant = useMemo(()=>{
|
|
27
|
+
if ('sm' === size) return 'body3Bold';
|
|
28
|
+
if ('lg' === size) return 'body1Bold';
|
|
29
|
+
return 'body2Bold';
|
|
30
|
+
}, [
|
|
31
|
+
size
|
|
32
|
+
]);
|
|
33
|
+
const animatedValue = useRef(new Animated.Value(isSelected ? 1 : 0)).current;
|
|
34
|
+
useEffect(()=>{
|
|
35
|
+
Animated.timing(animatedValue, {
|
|
36
|
+
toValue: isSelected ? 1 : 0,
|
|
37
|
+
duration: 200,
|
|
38
|
+
useNativeDriver: false
|
|
39
|
+
}).start();
|
|
40
|
+
}, [
|
|
41
|
+
isSelected,
|
|
42
|
+
animatedValue
|
|
43
|
+
]);
|
|
44
|
+
const thumbTranslateX = useMemo(()=>{
|
|
45
|
+
const padding = 'lg' === size ? ms(3) : ms(2);
|
|
46
|
+
return animatedValue.interpolate({
|
|
47
|
+
inputRange: [
|
|
48
|
+
0,
|
|
49
|
+
1
|
|
50
|
+
],
|
|
51
|
+
outputRange: [
|
|
52
|
+
padding,
|
|
53
|
+
styles.track.width - styles.thumb.width - padding
|
|
54
|
+
]
|
|
55
|
+
});
|
|
56
|
+
}, [
|
|
57
|
+
animatedValue,
|
|
58
|
+
styles,
|
|
59
|
+
size
|
|
60
|
+
]);
|
|
61
|
+
const handlePress = ()=>{
|
|
62
|
+
if (isReadOnly || isDisabled) return;
|
|
63
|
+
setIsSelected(!isSelected);
|
|
64
|
+
onValueChange?.(!isSelected);
|
|
65
|
+
};
|
|
66
|
+
return /*#__PURE__*/ jsxs(UXTouchableOpacity, {
|
|
67
|
+
activeOpacity: 0.8,
|
|
68
|
+
disabled: isDisabled || isReadOnly,
|
|
69
|
+
gap: gap,
|
|
70
|
+
items: "center",
|
|
71
|
+
opacity: isDisabled ? disabledOpacity : 1,
|
|
72
|
+
onPress: handlePress,
|
|
73
|
+
...flexProps,
|
|
74
|
+
children: [
|
|
75
|
+
/*#__PURE__*/ jsx(Animated.View, {
|
|
76
|
+
style: [
|
|
77
|
+
styles.track,
|
|
78
|
+
{
|
|
79
|
+
backgroundColor: isSelected ? getColor(color) : getColor('tertiary')
|
|
80
|
+
}
|
|
81
|
+
],
|
|
82
|
+
children: /*#__PURE__*/ jsx(Animated.View, {
|
|
83
|
+
style: [
|
|
84
|
+
styles.thumb,
|
|
85
|
+
{
|
|
86
|
+
transform: [
|
|
87
|
+
{
|
|
88
|
+
translateX: thumbTranslateX
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
})
|
|
94
|
+
}),
|
|
95
|
+
'string' == typeof children ? /*#__PURE__*/ jsx(Text, {
|
|
96
|
+
variant: labelVariant,
|
|
97
|
+
children: children
|
|
98
|
+
}) : children
|
|
99
|
+
]
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
UXSwitch.displayName = 'UXSwitch';
|
|
103
|
+
export { UXSwitch };
|