Skip to content

Commit 832833e

Browse files
committed
Merge pull request #31 from sangyuo/feat/suspense-loading
feat: atomic
2 parents 1e90bac + 32ea17b commit 832833e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+509
-254
lines changed

.eslintrc.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
module.exports = {
22
root: true,
33
extends: '@react-native',
4+
plugins: {
5+
prettier: true,
6+
},
47
rules: {
58
'react/no-unstable-nested-components': [
69
'off',
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import {useRef, useEffect} from 'react';
2+
import {Animated} from 'react-native';
3+
4+
interface Props {
5+
fromValue?: number;
6+
toValue?: number;
7+
duration?: number;
8+
useNativeDriver?: boolean;
9+
animationType?: 'shine' | 'opacity';
10+
disableAnimation?: boolean;
11+
}
12+
13+
const useAnimationPlaceholder = ({
14+
fromValue = 0,
15+
toValue = 100,
16+
duration = 750,
17+
useNativeDriver = true,
18+
animationType = 'opacity',
19+
disableAnimation = false,
20+
}: Props) => {
21+
const animationValue = useRef(new Animated.Value(fromValue)).current;
22+
23+
useEffect(() => {
24+
const animations = {
25+
opacity: [
26+
Animated.timing(animationValue, {
27+
toValue,
28+
duration,
29+
useNativeDriver,
30+
}),
31+
Animated.timing(animationValue, {
32+
toValue: fromValue,
33+
duration,
34+
useNativeDriver,
35+
}),
36+
],
37+
shine: [
38+
Animated.timing(animationValue, {
39+
toValue: toValue,
40+
duration,
41+
useNativeDriver,
42+
}),
43+
],
44+
};
45+
const animation = Animated.loop(
46+
Animated.sequence(animations[animationType]),
47+
);
48+
if (!disableAnimation) {
49+
animation.start();
50+
}
51+
return () => {
52+
animation.stop();
53+
};
54+
}, [disableAnimation]);
55+
56+
if (animationType === 'opacity') {
57+
return {
58+
opacity: animationValue.interpolate({
59+
inputRange: [0, 100],
60+
outputRange: [0, 1],
61+
}),
62+
};
63+
}
64+
const left = animationValue.interpolate({
65+
inputRange: [0, 100],
66+
outputRange: ['0%', '75%'],
67+
});
68+
69+
return {left};
70+
};
71+
72+
export default useAnimationPlaceholder;

src/components/button/index.tsx renamed to src/atomic/atoms/Box.tsx

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,38 @@
11
import {
2-
Pressable,
3-
PressableProps,
4-
TouchableHighlight,
2+
ImageBackground,
3+
ImageBackgroundProps,
4+
TextInput,
5+
TextInputProps,
6+
Text,
7+
TextProps,
8+
View,
9+
ViewProps,
10+
TouchableOpacityProps,
11+
TouchableOpacity,
512
TouchableHighlightProps,
6-
TouchableNativeFeedback,
13+
TouchableHighlight,
714
TouchableNativeFeedbackProps,
8-
TouchableOpacity,
9-
TouchableOpacityProps,
10-
TouchableWithoutFeedback,
15+
TouchableNativeFeedback,
1116
TouchableWithoutFeedbackProps,
12-
View,
17+
TouchableWithoutFeedback,
18+
PressableProps,
19+
Pressable,
20+
Animated,
1321
} from 'react-native';
14-
import {withButtonBox} from '../../hoc';
22+
import {withButtonBox, withElementBox} from '../../hoc';
23+
24+
export const Box = withElementBox<ViewProps, View>(View);
25+
export const AnimationBox = withElementBox<ViewProps, typeof Animated.View>(
26+
Animated.View,
27+
);
28+
export const TextBox = withElementBox<TextProps, Text>(Text);
29+
export const TextInputBoxBase = withElementBox<TextInputProps, TextInput>(
30+
TextInput,
31+
);
32+
export const ImageBackgroundBox = withElementBox<
33+
ImageBackgroundProps,
34+
ImageBackground
35+
>(ImageBackground);
1536

1637
export const ButtonBox = withButtonBox<
1738
TouchableOpacityProps,

src/atomic/atoms/Icons.tsx

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import React from 'react';
2+
import {Path, SvgProps} from 'react-native-svg';
3+
import {SvgProvider} from '../../provider';
4+
5+
export const ArrowDown = ({
6+
...props
7+
}: SvgProps & {
8+
width?: number;
9+
}) => {
10+
return (
11+
<SvgProvider {...props} originalHeight={14}>
12+
<Path
13+
clipRule="evenodd"
14+
fillRule="evenodd"
15+
d="M23.0631 0.451798C22.4773 -0.150273 21.5276 -0.150273 20.9418 0.451798L12.0024 9.63966L3.0631 0.451797C2.47732 -0.150274 1.52757 -0.150274 0.941782 0.451797C0.355995 1.05387 0.355995 2.03002 0.941782 2.63209L12.0024 14.0002L23.0631 2.63209C23.6489 2.03002 23.6489 1.05387 23.0631 0.451798Z"
16+
/>
17+
</SvgProvider>
18+
);
19+
};
20+
21+
export const ArrowLeft = ({
22+
...props
23+
}: SvgProps & {
24+
width?: number;
25+
}) => {
26+
return (
27+
<SvgProvider {...props} originalHeight={24} originalWidth={14}>
28+
<Path
29+
clipRule="evenodd"
30+
fillRule="evenodd"
31+
d="M13.5484 0.458442C14.1505 1.0697 14.1505 2.06074 13.5484 2.67199L4.36059 12L13.5484 21.328C14.1505 21.9393 14.1505 22.9303 13.5484 23.5416C12.9464 24.1528 11.9702 24.1528 11.3682 23.5416L0 12L11.3682 0.458442C11.9702 -0.152814 12.9464 -0.152814 13.5484 0.458442Z"
32+
/>
33+
</SvgProvider>
34+
);
35+
};
36+
37+
export const ArrowRight = ({
38+
...props
39+
}: SvgProps & {
40+
width?: number;
41+
}) => {
42+
return (
43+
<SvgProvider {...props} originalHeight={24} originalWidth={14}>
44+
<Path
45+
clipRule="evenodd"
46+
fillRule="evenodd"
47+
d="M0.451553 0.458442C-0.150518 1.0697 -0.150518 2.06074 0.451553 2.67199L9.63941 12L0.451553 21.328C-0.150518 21.9393 -0.150518 22.9303 0.451553 23.5416C1.05363 24.1528 2.02978 24.1528 2.63185 23.5416L14 12L2.63185 0.458442C2.02978 -0.152814 1.05363 -0.152814 0.451553 0.458442Z"
48+
/>
49+
</SvgProvider>
50+
);
51+
};
52+
53+
export const ArrowUp = ({
54+
...props
55+
}: SvgProps & {
56+
width?: number;
57+
}) => {
58+
return (
59+
<SvgProvider {...props} originalHeight={14}>
60+
<Path
61+
clipRule="evenodd"
62+
fillRule="evenodd"
63+
d="M23.5416 13.5484C22.9303 14.1505 21.9393 14.1505 21.328 13.5484L12 4.36059L2.67199 13.5484C2.06074 14.1505 1.0697 14.1505 0.458442 13.5484C-0.152814 12.9464 -0.152814 11.9702 0.458442 11.3682L12 0L23.5416 11.3682C24.1528 11.9702 24.1528 12.9464 23.5416 13.5484Z"
64+
/>
65+
</SvgProvider>
66+
);
67+
};
68+
69+
export const IconClose = ({
70+
...props
71+
}: SvgProps & {
72+
width?: number;
73+
}) => {
74+
return (
75+
<SvgProvider {...props} originalHeight={21} originalWidth={21}>
76+
<Path
77+
clipRule="evenodd"
78+
fillRule="evenodd"
79+
d="M18.9432 0.939584C19.529 0.353798 20.4788 0.353798 21.0646 0.939584C21.6504 1.52537 21.6504 2.47512 21.0646 3.0609L13.1252 11.0002L21.0646 18.9396C21.6504 19.5254 21.6504 20.4751 21.0646 21.0609C20.4788 21.6467 19.529 21.6467 18.9432 21.0609L11.0039 13.1216L3.06457 21.0609C2.47878 21.6467 1.52903 21.6467 0.943247 21.0609C0.35746 20.4751 0.35746 19.5254 0.943247 18.9396L8.88259 11.0002L0.943246 3.0609C0.35746 2.47512 0.35746 1.52537 0.943246 0.939585C1.52903 0.353798 2.47878 0.353798 3.06457 0.939585L11.0039 8.87892L18.9432 0.939584Z"
80+
/>
81+
</SvgProvider>
82+
);
83+
};
84+
85+
export const IconSearch = ({
86+
...props
87+
}: SvgProps & {
88+
width?: number;
89+
}) => {
90+
return (
91+
<SvgProvider {...props} originalHeight={23} originalWidth={23}>
92+
<Path
93+
clipRule="evenodd"
94+
fillRule="evenodd"
95+
d="M9.6405 0.5C4.59625 0.5 0.5 4.55659 0.5 9.56934C0.5 14.5821 4.59625 18.6387 9.6405 18.6387C11.5211 18.6387 13.2705 18.0746 14.7244 17.1075L20.6875 23.0214C21.3309 23.6595 22.3724 23.6595 23.0157 23.0214C23.6614 22.3811 23.6614 21.3411 23.0157 20.7007L17.0908 14.8247C18.1547 13.3423 18.781 11.528 18.781 9.56934C18.781 4.55659 14.6848 0.5 9.6405 0.5ZM3.79675 9.56934C3.79675 6.37467 6.40918 3.77783 9.6405 3.77783C12.8718 3.77783 15.4843 6.37467 15.4843 9.56934C15.4843 12.764 12.8718 15.3608 9.6405 15.3608C6.40918 15.3608 3.79675 12.764 3.79675 9.56934Z"
96+
/>
97+
</SvgProvider>
98+
);
99+
};
100+
101+
export const Tick = ({
102+
...props
103+
}: SvgProps & {
104+
width?: number;
105+
}) => {
106+
return (
107+
<SvgProvider {...props} originalHeight={17}>
108+
<Path
109+
clipRule="evenodd"
110+
fillRule="evenodd"
111+
d="M23.5127 0.602108C24.1761 1.27861 24.1663 2.36564 23.4908 3.03005L9.19871 17.0883L0.516979 8.54862C-0.158495 7.8842 -0.168274 6.79717 0.495138 6.12067C1.15855 5.44417 2.24393 5.43438 2.91941 6.0988L9.19871 12.2753L21.0884 0.580234C21.7639 -0.0841842 22.8493 -0.0743912 23.5127 0.602108Z"
112+
/>
113+
</SvgProvider>
114+
);
115+
};

src/components/image/index.tsx renamed to src/atomic/atoms/ImageBox.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import {ImageBoxProps, ImageModuleType} from '../../model';
55
import {classNames, isNumber} from '../../utils';
66
import useLoadModuleFastImage from '../../hook/useLoadModuleFastImage';
77
import {SvgUri} from 'react-native-svg';
8-
import {Box} from '../..';
8+
import {Box} from './Box';
99

10-
function ImageComponent({
10+
export function ImageBox({
1111
className,
1212
imageModuleType = ImageModuleType.Image,
1313
source,
@@ -40,5 +40,4 @@ function ImageComponent({
4040
/>
4141
);
4242
}
43-
44-
export default ImageComponent;
43+
export default ImageBox;

src/atomic/atoms/Placeholder.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import React, {useMemo} from 'react';
2+
import {AnimationBox} from './Box';
3+
import {classNames, randomIndex} from '../../utils';
4+
import {PlaceholderProps} from '../../model';
5+
import useAnimationPlaceholder from '../../animations/useAnimationPlaceholder';
6+
7+
const styled = {
8+
line: 'h-3',
9+
media: 'w-12 h-12',
10+
};
11+
12+
const widthRatios = [
13+
'w-full',
14+
'w-1/2',
15+
'w-1/3',
16+
'w-2/3',
17+
'w-1/4',
18+
'w-3/4',
19+
'w-1/5',
20+
'w-2/5',
21+
'w-3/5',
22+
'w-4/5',
23+
];
24+
25+
export const Placeholder = ({
26+
className,
27+
placeholderType = 'line',
28+
randomWidth,
29+
animationType = 'opacity',
30+
disableAnimation = false,
31+
classShine,
32+
children,
33+
}: PlaceholderProps) => {
34+
const animationStyled = useAnimationPlaceholder({
35+
animationType,
36+
useNativeDriver: animationType !== 'shine',
37+
disableAnimation,
38+
fromValue: disableAnimation ? 100 : 0,
39+
});
40+
const classWidthRandom = useMemo(
41+
() => (randomWidth ? widthRatios[randomIndex(widthRatios.length)] : ''),
42+
[randomWidth],
43+
);
44+
45+
return (
46+
<AnimationBox
47+
style={animationType === 'opacity' ? animationStyled : undefined}
48+
className={classNames(
49+
'w-full bg-gray-400 relative',
50+
placeholderType !== 'none' ? styled[placeholderType] : '',
51+
classWidthRandom,
52+
className,
53+
)}>
54+
{animationType === 'shine' && (
55+
<AnimationBox
56+
style={animationStyled}
57+
className={classNames('bg-white h-full w-1/4 opacity-40', classShine)}
58+
/>
59+
)}
60+
{children}
61+
</AnimationBox>
62+
);
63+
};

src/atomic/atoms/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './Box';
2+
export * from './Icons';
3+
export * from './ImageBox';
4+
export * from './Placeholder';

src/atomic/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './atoms';
2+
export * from './molecules';
3+
export * from './organisms';

src/components/checkbox/index.tsx renamed to src/atomic/molecules/Checkbox.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import React, {useMemo} from 'react';
2-
import {Box, ButtonBox, classNames, TextBox} from '../..';
32
import {CheckboxProps} from '../../model';
43
import {useVarianCheckbox} from '../../hook';
5-
import {Tick} from '../svgBox/Tick';
4+
import {Box, ButtonBox, TextBox, Tick} from '..';
5+
import {classNames} from '../../utils';
66

7-
function Checkbox<ItemT = any>(props: CheckboxProps<ItemT>) {
7+
export function Checkbox<ItemT = any>(props: CheckboxProps<ItemT>) {
88
const {
99
className,
1010
size,
@@ -78,4 +78,3 @@ function Checkbox<ItemT = any>(props: CheckboxProps<ItemT>) {
7878
</ButtonBox>
7979
);
8080
}
81-
export default Checkbox;

0 commit comments

Comments
 (0)