Skip to content

Commit cf1d62c

Browse files
committed
Merge branch 'dev'
2 parents 75e2a2f + 2a8ed59 commit cf1d62c

File tree

10 files changed

+290
-10
lines changed

10 files changed

+290
-10
lines changed

example/examples/src/routes.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,4 +530,12 @@ export const stackPageData: Routes[] = [
530530
description: '图片头部组件',
531531
},
532532
},
533+
{
534+
name: 'Skeleton',
535+
component: require('./routes/Skeleton').default,
536+
params: {
537+
title: 'Skeleton 骨架屏组件',
538+
description: '骨架屏组件',
539+
},
540+
},
533541
];
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {Skeleton} from '@uiw/react-native';
2+
import React, {useState, useEffect} from 'react';
3+
import {StyleSheet, View, Text, TouchableOpacity} from 'react-native';
4+
import Layout, {Container} from '../../Layout';
5+
6+
const {Header, Body, Card, Footer} = Layout;
7+
8+
const App = (props: any) => {
9+
const [loading, setLoading] = useState(true);
10+
const [data, setData] = useState<string[]>(['', '', '']);
11+
useEffect(() => {
12+
fetchData();
13+
}, []);
14+
15+
const fetchData = () => {
16+
setLoading(true);
17+
setTimeout(() => {
18+
setData(['项目1', '项目2', '项目3']);
19+
setLoading(false);
20+
}, 3000);
21+
};
22+
const {route} = props;
23+
const description = route.params.description;
24+
const title = route.params.title;
25+
26+
const skeletonStyles = [styles.item, styles.item, styles.item];
27+
28+
return (
29+
<Container>
30+
<Layout>
31+
<Header title={title} description={description} />
32+
<Body style={{paddingLeft: 16, paddingRight: 16}}>
33+
<Card title="基础实例">
34+
<View style={styles.container}>
35+
{loading ? (
36+
<Skeleton loading={loading} styles={skeletonStyles} duration={1200} containerStyle={styles.list} />
37+
) : (
38+
<View style={styles.list}>
39+
{data.map((item, index) => (
40+
<Text key={index} style={styles.item}>
41+
{item}
42+
</Text>
43+
))}
44+
</View>
45+
)}
46+
<TouchableOpacity style={styles.button} onPress={fetchData}>
47+
<Text style={styles.buttonText}>重新加载</Text>
48+
</TouchableOpacity>
49+
</View>
50+
</Card>
51+
</Body>
52+
<Footer />
53+
</Layout>
54+
</Container>
55+
);
56+
};
57+
58+
const styles = StyleSheet.create({
59+
container: {
60+
flex: 1,
61+
justifyContent: 'center',
62+
alignItems: 'center',
63+
},
64+
list: {
65+
width: '100%',
66+
},
67+
item: {
68+
backgroundColor: '#E1E9EE',
69+
height: 30,
70+
marginBottom: 10,
71+
justifyContent: 'center',
72+
alignItems: 'center',
73+
},
74+
button: {
75+
backgroundColor: '#007AFF',
76+
paddingHorizontal: 20,
77+
paddingVertical: 10,
78+
borderRadius: 5,
79+
},
80+
buttonText: {
81+
color: '#FFF',
82+
fontSize: 16,
83+
},
84+
});
85+
86+
export default App;

example/examples/src/routes/VerificationCode/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ const VerificationCodeDemo: React.FC<VerificationCodeProps> = ({route}) => {
7373
<Card title="发验证码之前的回调">
7474
<VerificationCode value={value} count={3} onChange={(text: string) => onChange(text)} onBefore={onBefore} outerStyle={{borderColor: '#ccc'}} />
7575
</Card>
76-
<Card title="发送验证码">
76+
<Card title="发验证码时的回调">
7777
<VerificationCode value={value} count={3} onChange={(text: string) => onChange(text)} onSend={onSend} outerStyle={{borderColor: '#ccc'}} />
7878
</Card>
7979
<Card title="倒计时结束后的回调">

packages/core/src/Form/comps/input.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import React from 'react';
2-
import { TextInput, TextInputProps } from 'react-native';
3-
import { useTheme } from "@shopify/restyle"
4-
import { Theme } from '../../theme'
2+
import { Platform, TextInput, TextInputProps } from 'react-native';
3+
import { useTheme } from '@shopify/restyle';
4+
import { Theme } from '../../theme';
55

66
const Input = ({ value, onChange, ...others }: TextInputProps & { onChange?: (value: string) => void }) => {
7-
const theme = useTheme<Theme>()
7+
const theme = useTheme<Theme>();
8+
const isIOS = Platform.OS === 'ios';
89
return (
910
<TextInput
1011
value={value}
1112
onChangeText={(value) => {
1213
onChange?.(value);
1314
}}
14-
style={{ color: theme.colors.text }}
15+
style={{ paddingVertical: isIOS ? 5 : -5, color: theme.colors.text }}
1516
{...others}
1617
/>
1718
);

packages/core/src/Form/comps/label.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const styles = StyleSheet.create({
2020
width: 'auto',
2121
fontSize: 16,
2222
fontWeight: '500',
23-
marginBottom: 10,
23+
marginBottom: 5,
2424
},
2525
});
2626

packages/core/src/Form/comps/tip.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useContext } from 'react';
22
import { Context } from '../hooks/context';
33
import { FormItemsProps } from '../types';
4-
import { Text } from 'react-native';
4+
import { View, Text } from 'react-native';
55

66
const Tip = ({ v }: { v: Partial<FormItemsProps> & { field: string } }) => {
77
const {
@@ -11,7 +11,11 @@ const Tip = ({ v }: { v: Partial<FormItemsProps> & { field: string } }) => {
1111
const content = validator.message(v.field, store[v.field], {
1212
validate: v?.validate,
1313
});
14-
return <Text style={{ color: 'red', marginBottom: content && 10, marginTop: content && 10 }}>{content}</Text>;
14+
return (
15+
<View style={{ marginTop: 5 }}>
16+
{content && <Text style={{ color: 'red', marginBottom: content && 5 }}>{content}</Text>}
17+
</View>
18+
);
1519
};
1620

1721
export default Tip;

packages/core/src/Form/styles.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ const styles = StyleSheet.create({
1010
},
1111
form_items: {
1212
textAlign: 'center',
13-
margin: 10,
13+
margin: 5,
1414
borderBottomWidth: 0.5,
1515
borderBottomColor: '#ccc',
1616
},

packages/core/src/Skeleton/README.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
Skeleton 骨架屏
2+
---
3+
4+
在需要等待加载内容的位置提供一个占位图形组合。
5+
6+
### 基础示例
7+
8+
```jsx mdx:preview&background=#bebebe29
9+
import { Skeleton } from '@uiw/react-native';
10+
import React, { useState, useEffect } from 'react';
11+
import { StyleSheet, View, Text, TouchableOpacity } from 'react-native';
12+
13+
const Demo = () => {
14+
const [loading, setLoading] = useState(true);
15+
const [data, setData] = useState(['', '', '']);
16+
useEffect(() => {
17+
fetchData();
18+
}, []);
19+
20+
const fetchData = () => {
21+
setLoading(true);
22+
setTimeout(() => {
23+
setData(['项目1', '项目2', '项目3']);
24+
setLoading(false);
25+
}, 3000);
26+
};
27+
28+
const skeletonStyles = [
29+
styles.item,
30+
styles.item,
31+
styles.item,
32+
];
33+
34+
return (
35+
<View style={styles.container}>
36+
{loading ? (
37+
<Skeleton
38+
loading={loading}
39+
styles={skeletonStyles}
40+
duration={1200}
41+
containerStyle={styles.list}
42+
/>
43+
) : (
44+
<View style={styles.list}>
45+
{data.map((item, index) => (
46+
<Text key={index} style={styles.item}>
47+
{item}
48+
</Text>
49+
))}
50+
</View>
51+
)}
52+
<TouchableOpacity style={styles.button} onPress={fetchData}>
53+
<Text style={styles.buttonText}>重新加载</Text>
54+
</TouchableOpacity>
55+
</View>
56+
);
57+
};
58+
59+
const styles = StyleSheet.create({
60+
container: {
61+
flex: 1,
62+
justifyContent: 'center',
63+
alignItems: 'center',
64+
},
65+
list: {
66+
width: '100%',
67+
paddingHorizontal: 20,
68+
},
69+
item: {
70+
backgroundColor: '#E1E9EE',
71+
height: 30,
72+
marginBottom: 10,
73+
justifyContent: 'center',
74+
alignItems: 'center',
75+
},
76+
button: {
77+
backgroundColor: '#007AFF',
78+
paddingHorizontal: 20,
79+
paddingVertical: 10,
80+
borderRadius: 5,
81+
},
82+
buttonText: {
83+
color: '#FFF',
84+
fontSize: 16,
85+
},
86+
});
87+
88+
export default Demo;
89+
90+
```
91+
92+
### Props
93+
94+
| 参数 | 必填 | 说明 | 类型 | 默认值 |
95+
| --- | --- | --- | --- | --- |
96+
| loading | `false` | 是否正在加载 | `boolean` | `true` |
97+
| styles | `true` | 骨架屏的样式 | `ViewStyle[]` | |
98+
| duration | `false` | 动画的执行速度 | `number` | `1200` |
99+
| easing | `false` | 动画的执行方式 | `Animated.EasingFunction` | `Easing.bezierFn(0.5, 0, 0.25, 1)` |
100+
| containerStyle | `false` | 容器样式 | `StyleProp<ViewStyle>` | |
101+
| animationType | `false` | 动画类型(条纹/脉搏/无) | `AnimationType` | `shiver` |
102+
| boneColor | `false` | 基础颜色 | `string` | `#E1E9EE` |
103+
| highlightColor | `false` | 高亮颜色 | `string` | `#F2F8FC` |

packages/core/src/Skeleton/index.tsx

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import React from 'react';
2+
import { View, Animated, EasingFunction, Easing, ViewStyle, StyleProp } from 'react-native';
3+
type AnimationType = 'none' | 'stripe' | 'pulse';
4+
5+
interface SkeletonProps {
6+
loading?: boolean;
7+
styles: ViewStyle[];
8+
duration?: number;
9+
easing?: EasingFunction;
10+
containerStyle?: StyleProp<ViewStyle>;
11+
animationType?: AnimationType;
12+
boneColor?: string;
13+
highlightColor?: string;
14+
}
15+
16+
const Skeleton: React.FC<SkeletonProps> = ({
17+
loading = true,
18+
styles,
19+
duration = 1200,
20+
easing = Easing.bezier(0.5, 0, 0.25, 1),
21+
containerStyle,
22+
animationType = 'stripe',
23+
boneColor = '#E1E9EE',
24+
highlightColor = '#F2F8FC',
25+
}) => {
26+
const animatedValue = React.useRef(new Animated.Value(0)).current;
27+
28+
const getInterpolatedColor = () => {
29+
return animatedValue.interpolate({
30+
inputRange: [0, 1],
31+
outputRange: [boneColor, highlightColor],
32+
});
33+
};
34+
const getAnimationStyle = () => {
35+
switch (animationType) {
36+
case 'stripe':
37+
return {
38+
backgroundColor: getInterpolatedColor(),
39+
};
40+
case 'pulse':
41+
return {
42+
opacity: animatedValue.interpolate({
43+
inputRange: [0, 0.5, 1],
44+
outputRange: [0.5, 1, 0.5],
45+
}),
46+
};
47+
default:
48+
return {};
49+
}
50+
};
51+
52+
React.useEffect(() => {
53+
if (loading) {
54+
Animated.loop(
55+
Animated.timing(animatedValue, {
56+
toValue: 1,
57+
duration,
58+
easing: easing,
59+
useNativeDriver: false,
60+
}),
61+
).start();
62+
} else {
63+
animatedValue.setValue(0);
64+
}
65+
}, [loading]);
66+
67+
return (
68+
<View style={containerStyle}>
69+
{styles.map((style, index) => (
70+
<Animated.View key={index} style={[style, getAnimationStyle()]} />
71+
))}
72+
</View>
73+
);
74+
};
75+
76+
export default Skeleton;

packages/core/src/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ export { default as LoginPage } from './LoginPage';
135135
export * from './LoginPage';
136136
export { default as ImageHeader } from './ImageHeader';
137137
export * from './ImageHeader';
138+
export { default as Skeleton } from './Skeleton';
139+
export * from './Skeleton';
138140
/**
139141
* Typography
140142
*/

0 commit comments

Comments
 (0)