@0610studio/zs-ui 0.0.13 → 0.0.15
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/build/index.d.ts +2 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -1
- package/build/index.js.map +1 -1
- package/build/model/index.d.ts +4 -0
- package/build/model/index.d.ts.map +1 -0
- package/build/model/index.js +4 -0
- package/build/model/index.js.map +1 -0
- package/build/types/index.d.ts +9 -0
- package/build/types/index.d.ts.map +1 -0
- package/build/types/index.js +2 -0
- package/build/types/index.js.map +1 -0
- package/package.json +6 -1
- package/.eslintrc.js +0 -5
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/vcs-1/gc.properties +0 -0
- package/android/build.gradle +0 -43
- package/android/src/main/AndroidManifest.xml +0 -2
- package/android/src/main/java/kr/co/studio0610/zsui/ZsUiModule.kt +0 -47
- package/android/src/main/java/kr/co/studio0610/zsui/ZsUiView.kt +0 -7
- package/expo-module.config.json +0 -9
- package/ios/ZsUi.podspec +0 -27
- package/ios/ZsUiModule.swift +0 -44
- package/ios/ZsUiView.swift +0 -7
- package/src/ZsUi.types.ts +0 -7
- package/src/ZsUiModule.ts +0 -5
- package/src/ZsUiModule.web.ts +0 -13
- package/src/ZsUiView.tsx +0 -11
- package/src/ZsUiView.web.tsx +0 -11
- package/src/assets/SvgCheck.tsx +0 -16
- package/src/assets/SvgX.tsx +0 -22
- package/src/index.ts +0 -52
- package/src/model/types.ts +0 -104
- package/src/model/useNotify.ts +0 -14
- package/src/model/useNotifyProvider.tsx +0 -259
- package/src/model/useThemeProvider.tsx +0 -99
- package/src/model/utils.ts +0 -31
- package/src/notify/AlertNotify/index.tsx +0 -172
- package/src/notify/BottomSheetNotify/index.tsx +0 -177
- package/src/notify/BottomSheetNotify/model/useBottomSheetNotify.tsx +0 -270
- package/src/notify/BottomSheetNotify/types/index.ts +0 -3
- package/src/notify/BottomSheetNotify/ui/BSTextInput/index.tsx +0 -28
- package/src/notify/BottomSheetNotify/ui/ContentsComponent/index.tsx +0 -86
- package/src/notify/LoadingNotify/index.tsx +0 -46
- package/src/notify/PopOver/PopOverButton.tsx +0 -56
- package/src/notify/PopOver/PopOverMenu.tsx +0 -95
- package/src/notify/SnackbarNotify/index.tsx +0 -43
- package/src/notify/SnackbarNotify/ui/SnackbarItem.tsx +0 -103
- package/src/notify/index.ts +0 -21
- package/src/notify/ui/ModalBackground.tsx +0 -46
- package/src/theme/index.ts +0 -3
- package/src/theme/palette.ts +0 -384
- package/src/theme/types.ts +0 -170
- package/src/theme/typography.ts +0 -199
- package/src/ui/ThrottleButton/index.tsx +0 -119
- package/src/ui/ZSBottomButton/index.tsx +0 -151
- package/src/ui/ZSContainer/index.tsx +0 -92
- package/src/ui/ZSPressable/index.tsx +0 -82
- package/src/ui/ZSRadioGroup/index.tsx +0 -141
- package/src/ui/ZSText/index.tsx +0 -22
- package/src/ui/ZSTextField/index.tsx +0 -200
- package/src/ui/ZSTextField/ui/ButtonClose.tsx +0 -20
- package/src/ui/ZSTextField/ui/ErrorComponent.tsx +0 -25
- package/src/ui/ZSView/index.tsx +0 -30
- package/src/ui/atoms/AnimatedWrapper.tsx +0 -90
- package/src/ui/atoms/ScrollViewAtom.tsx +0 -17
- package/src/ui/atoms/TextAtom.tsx +0 -15
- package/src/ui/atoms/ViewAtom.tsx +0 -15
- package/src/ui/index.ts +0 -27
- package/src/ui/types.ts +0 -12
- package/tsconfig.json +0 -9
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import React, { ReactNode, useState, useEffect } from 'react';
|
|
2
|
-
import { ViewProps, KeyboardAvoidingView, StatusBar, StyleSheet, Dimensions, ActivityIndicator, Platform, ScrollView } from 'react-native';
|
|
3
|
-
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
4
|
-
import ViewAtom from '../atoms/ViewAtom';
|
|
5
|
-
import ScrollViewAtom from '../atoms/ScrollViewAtom';
|
|
6
|
-
import { useTheme } from '../../model/useThemeProvider';
|
|
7
|
-
|
|
8
|
-
type ZSContainerProps = ViewProps & {
|
|
9
|
-
backgroundColor?: string;
|
|
10
|
-
isLoader?: boolean;
|
|
11
|
-
statusBarColor?: string;
|
|
12
|
-
barStyle?: 'light-content' | 'dark-content';
|
|
13
|
-
edges?: Array<'top' | 'right' | 'bottom' | 'left'>;
|
|
14
|
-
isScrollView?: boolean;
|
|
15
|
-
scrollViewRef?: React.RefObject<ScrollView>;
|
|
16
|
-
topComponent?: ReactNode;
|
|
17
|
-
bottomComponent?: ReactNode;
|
|
18
|
-
showsVerticalScrollIndicator?: boolean;
|
|
19
|
-
loadingComponent?: React.ReactNode;
|
|
20
|
-
keyboardVerticalOffset?: number;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
function ZSContainer({
|
|
24
|
-
backgroundColor,
|
|
25
|
-
isLoader = false,
|
|
26
|
-
statusBarColor,
|
|
27
|
-
barStyle = 'dark-content',
|
|
28
|
-
edges = ['top', 'bottom'],
|
|
29
|
-
isScrollView = true,
|
|
30
|
-
scrollViewRef,
|
|
31
|
-
topComponent,
|
|
32
|
-
bottomComponent,
|
|
33
|
-
showsVerticalScrollIndicator = true,
|
|
34
|
-
loadingComponent = <ActivityIndicator />,
|
|
35
|
-
keyboardVerticalOffset,
|
|
36
|
-
...props
|
|
37
|
-
}: ZSContainerProps) {
|
|
38
|
-
const { palette } = useTheme(); // 테마 사용
|
|
39
|
-
const [isDelayed, setIsDelayed] = useState(true);
|
|
40
|
-
|
|
41
|
-
useEffect(() => {
|
|
42
|
-
const timer = setTimeout(() => {
|
|
43
|
-
setIsDelayed(false);
|
|
44
|
-
}, 200);
|
|
45
|
-
return () => clearTimeout(timer);
|
|
46
|
-
}, []);
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<SafeAreaView style={[{ backgroundColor: backgroundColor || palette.background.base }, styles.flex1]} edges={edges}>
|
|
50
|
-
<StatusBar barStyle={barStyle} backgroundColor={statusBarColor || palette.background.base} />
|
|
51
|
-
|
|
52
|
-
{!isDelayed && (
|
|
53
|
-
<KeyboardAvoidingView
|
|
54
|
-
style={styles.flex1}
|
|
55
|
-
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
|
56
|
-
keyboardVerticalOffset={keyboardVerticalOffset}
|
|
57
|
-
enabled
|
|
58
|
-
>
|
|
59
|
-
{topComponent && topComponent}
|
|
60
|
-
|
|
61
|
-
{isLoader ? (
|
|
62
|
-
loadingComponent
|
|
63
|
-
) : isScrollView ? (
|
|
64
|
-
<ScrollViewAtom
|
|
65
|
-
ref={scrollViewRef}
|
|
66
|
-
style={styles.flex1}
|
|
67
|
-
bounces={false}
|
|
68
|
-
contentContainerStyle={styles.scrollContainerStyle}
|
|
69
|
-
showsVerticalScrollIndicator={showsVerticalScrollIndicator}
|
|
70
|
-
keyboardShouldPersistTaps="handled"
|
|
71
|
-
>
|
|
72
|
-
<ViewAtom style={[styles.flex1, props.style]}>
|
|
73
|
-
{props.children}
|
|
74
|
-
</ViewAtom>
|
|
75
|
-
</ScrollViewAtom>
|
|
76
|
-
) : (
|
|
77
|
-
<ViewAtom style={[styles.flex1, props.style]}>{props.children}</ViewAtom>
|
|
78
|
-
)}
|
|
79
|
-
|
|
80
|
-
{!isLoader && bottomComponent && bottomComponent}
|
|
81
|
-
</KeyboardAvoidingView>
|
|
82
|
-
)}
|
|
83
|
-
</SafeAreaView>
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
const styles = StyleSheet.create({
|
|
88
|
-
flex1: { flex: 1, width: Dimensions.get('window').width },
|
|
89
|
-
scrollContainerStyle: { flexGrow: 1, alignItems: 'center' },
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
export default ZSContainer;
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import React, { useCallback } from "react";
|
|
2
|
-
import { FlexAlignType, Pressable, ViewProps } from "react-native";
|
|
3
|
-
import Animated, { interpolate, useAnimatedStyle, useSharedValue, withTiming, runOnJS } from "react-native-reanimated";
|
|
4
|
-
import AnimatedWrapper from "../atoms/AnimatedWrapper";
|
|
5
|
-
import type { ShadowLevel } from "../types";
|
|
6
|
-
|
|
7
|
-
const DEFAULT_DURATION = { duration: 100 };
|
|
8
|
-
|
|
9
|
-
interface ZSPressableProps extends ViewProps {
|
|
10
|
-
onPress: (value?: any) => void;
|
|
11
|
-
onLongPress?: (value?: any) => void;
|
|
12
|
-
pressedBackgroundColor?: string;
|
|
13
|
-
pressedBackgroundBorderRadius?: number;
|
|
14
|
-
flex?: number;
|
|
15
|
-
minWidth?: number;
|
|
16
|
-
isAnimation?: boolean;
|
|
17
|
-
elevationLevel?: ShadowLevel;
|
|
18
|
-
fullWidth?: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function ZSPressable({
|
|
22
|
-
onPress,
|
|
23
|
-
onLongPress,
|
|
24
|
-
isAnimation = true,
|
|
25
|
-
pressedBackgroundColor = 'rgba(180, 180, 180, 0.1)',
|
|
26
|
-
pressedBackgroundBorderRadius = 16,
|
|
27
|
-
flex,
|
|
28
|
-
minWidth,
|
|
29
|
-
elevationLevel = 0,
|
|
30
|
-
fullWidth = false,
|
|
31
|
-
...props
|
|
32
|
-
}: ZSPressableProps) {
|
|
33
|
-
const isButtonPress = useSharedValue(0);
|
|
34
|
-
|
|
35
|
-
const boxAnimation = useAnimatedStyle(() => {
|
|
36
|
-
const scale = interpolate(
|
|
37
|
-
isButtonPress.value,
|
|
38
|
-
[0, 1],
|
|
39
|
-
[1, 0.96],
|
|
40
|
-
'clamp'
|
|
41
|
-
);
|
|
42
|
-
return {
|
|
43
|
-
transform: [{ scale: withTiming(scale, DEFAULT_DURATION) }],
|
|
44
|
-
};
|
|
45
|
-
}, []);
|
|
46
|
-
|
|
47
|
-
const handlePressStyle = useCallback(
|
|
48
|
-
(pressed: boolean) => {
|
|
49
|
-
runOnJS(() => {
|
|
50
|
-
isButtonPress.value = pressed ? 1 : 0;
|
|
51
|
-
})();
|
|
52
|
-
return {
|
|
53
|
-
backgroundColor: pressed ? pressedBackgroundColor : 'transparent',
|
|
54
|
-
borderRadius: pressedBackgroundBorderRadius,
|
|
55
|
-
flex: fullWidth ? 1 : flex,
|
|
56
|
-
minWidth: minWidth,
|
|
57
|
-
alignSelf: fullWidth ? 'stretch' : 'flex-start' as FlexAlignType,
|
|
58
|
-
};
|
|
59
|
-
},
|
|
60
|
-
[pressedBackgroundColor, pressedBackgroundBorderRadius, flex, minWidth, fullWidth]
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<Pressable
|
|
65
|
-
onPress={onPress}
|
|
66
|
-
onLongPress={onLongPress}
|
|
67
|
-
style={({ pressed }) => handlePressStyle(pressed)}
|
|
68
|
-
>
|
|
69
|
-
<Animated.View style={boxAnimation}>
|
|
70
|
-
<AnimatedWrapper
|
|
71
|
-
isAnimation={isAnimation}
|
|
72
|
-
elevationLevel={elevationLevel}
|
|
73
|
-
style={props.style}
|
|
74
|
-
>
|
|
75
|
-
{props.children}
|
|
76
|
-
</AnimatedWrapper>
|
|
77
|
-
</Animated.View>
|
|
78
|
-
</Pressable>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export default ZSPressable;
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import React, { useCallback, memo } from 'react';
|
|
2
|
-
import { ViewProps } from 'react-native';
|
|
3
|
-
import { RadioOption } from '../types';
|
|
4
|
-
import ViewAtom from '../atoms/ViewAtom';
|
|
5
|
-
import ZSText, { ZSTextProps } from '../ZSText';
|
|
6
|
-
import ZSPressable from '../ZSPressable';
|
|
7
|
-
import { useTheme } from '../../model/useThemeProvider';
|
|
8
|
-
import { SvgCheck } from '../../assets/SvgCheck';
|
|
9
|
-
|
|
10
|
-
function ZSRadioGroup({
|
|
11
|
-
options,
|
|
12
|
-
value,
|
|
13
|
-
onSelect,
|
|
14
|
-
containerStyle,
|
|
15
|
-
valueStyle,
|
|
16
|
-
minWidth,
|
|
17
|
-
disabled = false,
|
|
18
|
-
fullWidth = false,
|
|
19
|
-
selectStyle,
|
|
20
|
-
}: {
|
|
21
|
-
options: RadioOption[];
|
|
22
|
-
value?: RadioOption;
|
|
23
|
-
onSelect: (value: RadioOption) => void;
|
|
24
|
-
containerStyle?: ViewProps;
|
|
25
|
-
valueStyle?: ZSTextProps;
|
|
26
|
-
selectStyle?: ZSTextProps;
|
|
27
|
-
minWidth?: number;
|
|
28
|
-
disabled?: boolean;
|
|
29
|
-
fullWidth?: boolean;
|
|
30
|
-
}) {
|
|
31
|
-
const { palette } = useTheme();
|
|
32
|
-
|
|
33
|
-
const handleSelect = useCallback(
|
|
34
|
-
(option: RadioOption) => {
|
|
35
|
-
if (!disabled) {
|
|
36
|
-
onSelect(option);
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
[disabled, onSelect]
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
return (
|
|
43
|
-
<ViewAtom
|
|
44
|
-
style={{
|
|
45
|
-
flexDirection: fullWidth ? 'column' : 'row',
|
|
46
|
-
gap: 10,
|
|
47
|
-
flexWrap: fullWidth ? 'nowrap' : 'wrap',
|
|
48
|
-
width: '100%',
|
|
49
|
-
}}
|
|
50
|
-
{...containerStyle}
|
|
51
|
-
>
|
|
52
|
-
{options.map((option) => {
|
|
53
|
-
const isSelected = value?.index === option.index;
|
|
54
|
-
const setColor = isSelected ? palette.primary.light : palette.background.neutral;
|
|
55
|
-
|
|
56
|
-
return (
|
|
57
|
-
<ZSPressable
|
|
58
|
-
key={option.index}
|
|
59
|
-
onPress={() => handleSelect(option)}
|
|
60
|
-
pressedBackgroundColor="transparent"
|
|
61
|
-
flex={!minWidth ? 1 : undefined}
|
|
62
|
-
minWidth={minWidth}
|
|
63
|
-
fullWidth
|
|
64
|
-
>
|
|
65
|
-
<ViewAtom
|
|
66
|
-
style={{
|
|
67
|
-
flexDirection: 'row',
|
|
68
|
-
alignItems: 'center',
|
|
69
|
-
paddingVertical: 12,
|
|
70
|
-
borderWidth: 1,
|
|
71
|
-
paddingLeft: 10,
|
|
72
|
-
paddingRight: 15,
|
|
73
|
-
borderRadius: 26,
|
|
74
|
-
borderColor: setColor,
|
|
75
|
-
backgroundColor: isSelected ? palette.background.layer1 : 'transparent',
|
|
76
|
-
flex: 1,
|
|
77
|
-
}}
|
|
78
|
-
>
|
|
79
|
-
{/* fullWidth가 false일 때 동그라미 체크박스 표시 */}
|
|
80
|
-
{!fullWidth && (
|
|
81
|
-
<ViewAtom
|
|
82
|
-
style={{
|
|
83
|
-
width: 20,
|
|
84
|
-
height: 20,
|
|
85
|
-
borderWidth: 1,
|
|
86
|
-
borderRadius: 10,
|
|
87
|
-
borderColor: setColor,
|
|
88
|
-
justifyContent: 'center',
|
|
89
|
-
alignItems: 'center',
|
|
90
|
-
}}
|
|
91
|
-
>
|
|
92
|
-
<ViewAtom
|
|
93
|
-
style={{
|
|
94
|
-
width: 12,
|
|
95
|
-
height: 12,
|
|
96
|
-
borderRadius: 6,
|
|
97
|
-
backgroundColor: setColor,
|
|
98
|
-
}}
|
|
99
|
-
/>
|
|
100
|
-
</ViewAtom>
|
|
101
|
-
)}
|
|
102
|
-
{/* 옵션 텍스트 표시 */}
|
|
103
|
-
<ZSText style={{ marginLeft: 10 }} {...valueStyle}>
|
|
104
|
-
{option.value}
|
|
105
|
-
</ZSText>
|
|
106
|
-
|
|
107
|
-
{/* fullWidth가 true일 때 우측에 선택 버튼 표시 */}
|
|
108
|
-
{fullWidth && (
|
|
109
|
-
<ViewAtom style={{ flex: 1, flexDirection: 'row', justifyContent: 'flex-end' }}>
|
|
110
|
-
<ViewAtom
|
|
111
|
-
style={{
|
|
112
|
-
backgroundColor: isSelected
|
|
113
|
-
? palette.primary.main
|
|
114
|
-
: palette.background.layer2,
|
|
115
|
-
paddingHorizontal: 10,
|
|
116
|
-
borderRadius: 50,
|
|
117
|
-
minWidth: 42,
|
|
118
|
-
minHeight: 24,
|
|
119
|
-
justifyContent: 'center',
|
|
120
|
-
alignItems: 'center',
|
|
121
|
-
}}
|
|
122
|
-
>
|
|
123
|
-
{isSelected ? (
|
|
124
|
-
<SvgCheck size={18} />
|
|
125
|
-
) : (
|
|
126
|
-
<ZSText typo="body.5" {...selectStyle}>
|
|
127
|
-
선택
|
|
128
|
-
</ZSText>
|
|
129
|
-
)}
|
|
130
|
-
</ViewAtom>
|
|
131
|
-
</ViewAtom>
|
|
132
|
-
)}
|
|
133
|
-
</ViewAtom>
|
|
134
|
-
</ZSPressable>
|
|
135
|
-
);
|
|
136
|
-
})}
|
|
137
|
-
</ViewAtom>
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export default memo(ZSRadioGroup);
|
package/src/ui/ZSText/index.tsx
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import React, { memo } from 'react';
|
|
2
|
-
import { TextProps } from "react-native/types";
|
|
3
|
-
import { useTheme } from "../../model/useThemeProvider";
|
|
4
|
-
import { TypoOptions, TypoStyle, TextColorOptions, TypoSubStyle } from "../../theme/types";
|
|
5
|
-
import TextAtom from "../atoms/TextAtom"
|
|
6
|
-
|
|
7
|
-
export interface ZSTextProps extends TextProps {
|
|
8
|
-
typo?: TypoOptions;
|
|
9
|
-
color?: TextColorOptions;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function ZSText({
|
|
13
|
-
typo = 'body.2',
|
|
14
|
-
color = 'primary',
|
|
15
|
-
...props
|
|
16
|
-
}: ZSTextProps) {
|
|
17
|
-
const { palette, typography } = useTheme();
|
|
18
|
-
const [s01, s02] = typo.split('.') as [TypoStyle, TypoSubStyle];
|
|
19
|
-
return <TextAtom {...props} style={[typography[s01][s02], { color: palette.text[color] }, props.style]}>{props.children}</TextAtom>
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export default memo(ZSText);
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import React, { useMemo, useCallback, useState, useEffect } from 'react';
|
|
2
|
-
import { LayoutChangeEvent, StyleProp, TextInput, TextInputProps, TextStyle, ViewStyle } from 'react-native';
|
|
3
|
-
import Animated, { interpolate, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';
|
|
4
|
-
import ButtonClose from './ui/ButtonClose';
|
|
5
|
-
import ErrorComponent from './ui/ErrorComponent';
|
|
6
|
-
import { TypoOptions, TypoStyle, TypoSubStyle } from '../../theme';
|
|
7
|
-
import { extractStyle } from '../../model/utils';
|
|
8
|
-
import { useTheme } from '../../model/useThemeProvider';
|
|
9
|
-
import ViewAtom from '../atoms/ViewAtom';
|
|
10
|
-
|
|
11
|
-
export type BoxStyle = 'outline' | 'underline' | 'inbox';
|
|
12
|
-
|
|
13
|
-
interface TextFieldProps {
|
|
14
|
-
typo?: TypoOptions;
|
|
15
|
-
status?: 'default' | 'error';
|
|
16
|
-
value: string;
|
|
17
|
-
onChangeText?: (text: string) => void;
|
|
18
|
-
inputBgColor?: string;
|
|
19
|
-
labelBgColor?: string;
|
|
20
|
-
label?: string;
|
|
21
|
-
labelColor?: string;
|
|
22
|
-
placeHolderColor?: string;
|
|
23
|
-
fontSize?: number;
|
|
24
|
-
borderColor?: string;
|
|
25
|
-
borderRadius?: number;
|
|
26
|
-
focusColor?: string;
|
|
27
|
-
errorColor?: string;
|
|
28
|
-
paddingHorizontal?: number;
|
|
29
|
-
borderWidth?: number;
|
|
30
|
-
errorMessage?: string;
|
|
31
|
-
textInputProps?: TextInputProps;
|
|
32
|
-
boxStyle?: BoxStyle;
|
|
33
|
-
innerBoxStyle?: 'top' | 'middle' | 'bottom';
|
|
34
|
-
disabled?: boolean;
|
|
35
|
-
allowFontScaling?: boolean;
|
|
36
|
-
isTextArea?: boolean;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function ZSTextField({
|
|
40
|
-
typo = 'body.2',
|
|
41
|
-
status = 'default',
|
|
42
|
-
value,
|
|
43
|
-
onChangeText,
|
|
44
|
-
label = 'Placeholder',
|
|
45
|
-
labelColor = '#757575',
|
|
46
|
-
placeHolderColor = '#B1B1B1',
|
|
47
|
-
inputBgColor = 'white',
|
|
48
|
-
labelBgColor = 'white',
|
|
49
|
-
borderWidth = 1.2,
|
|
50
|
-
borderColor = '#E7EDF0',
|
|
51
|
-
focusColor = '#007AFF',
|
|
52
|
-
errorColor = '#FF3B30',
|
|
53
|
-
borderRadius = 14,
|
|
54
|
-
paddingHorizontal = 15,
|
|
55
|
-
errorMessage,
|
|
56
|
-
textInputProps,
|
|
57
|
-
boxStyle = 'outline',
|
|
58
|
-
innerBoxStyle,
|
|
59
|
-
disabled = false,
|
|
60
|
-
allowFontScaling = true,
|
|
61
|
-
isTextArea = false,
|
|
62
|
-
}: TextFieldProps): JSX.Element {
|
|
63
|
-
const { typography, palette } = useTheme();
|
|
64
|
-
const [primaryStyle, subStyle] = typo.split('.') as [TypoStyle, TypoSubStyle];
|
|
65
|
-
|
|
66
|
-
// 폰트 크기 및 패밀리 추출
|
|
67
|
-
const fontSize = useMemo(() => extractStyle(typography[primaryStyle][subStyle], 'fontSize') as number || 17, [typography, primaryStyle, subStyle]);
|
|
68
|
-
const fontFamily = useMemo(() => extractStyle(typography[primaryStyle][subStyle], 'fontFamily') as string || '', [typography, primaryStyle, subStyle]);
|
|
69
|
-
|
|
70
|
-
// 컴포넌트 상태 관리
|
|
71
|
-
const [isFocused, setIsFocused] = useState<boolean>(false);
|
|
72
|
-
const labelAnimationValue = useSharedValue(0);
|
|
73
|
-
const boxHeightValue = useSharedValue(0);
|
|
74
|
-
|
|
75
|
-
// 포커스 및 값 변경 시 라벨 애니메이션 트리거
|
|
76
|
-
useEffect(() => {
|
|
77
|
-
const shouldAnimate = value !== '' || isFocused;
|
|
78
|
-
labelAnimationValue.value = withTiming(shouldAnimate ? 1 : 0, { duration: 50 });
|
|
79
|
-
}, [value, isFocused, labelAnimationValue]);
|
|
80
|
-
|
|
81
|
-
// 라벨 애니메이션 스타일
|
|
82
|
-
const animatedLabelStyle = useAnimatedStyle(() => {
|
|
83
|
-
const labelFontSize = interpolate(
|
|
84
|
-
labelAnimationValue.value,
|
|
85
|
-
[0, 1],
|
|
86
|
-
[fontSize + (boxStyle === 'inbox' ? 5 : 0), boxStyle === 'inbox' ? 10 : 12],
|
|
87
|
-
'clamp'
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
const labelTop = interpolate(
|
|
91
|
-
labelAnimationValue.value,
|
|
92
|
-
[0, 1],
|
|
93
|
-
[
|
|
94
|
-
isTextArea ? 12 : 0,
|
|
95
|
-
isTextArea ? -12 : -(boxHeightValue.value / 2) - 1 + (boxStyle === 'inbox' ? 18 : 0),
|
|
96
|
-
],
|
|
97
|
-
'clamp'
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
top: labelTop,
|
|
102
|
-
fontSize: labelFontSize,
|
|
103
|
-
};
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
// 레이아웃 핸들러
|
|
107
|
-
const handleLayout = useCallback((event: LayoutChangeEvent) => {
|
|
108
|
-
const { height } = event.nativeEvent.layout;
|
|
109
|
-
if (boxHeightValue.value !== height) boxHeightValue.value = height;
|
|
110
|
-
}, [boxHeightValue]);
|
|
111
|
-
|
|
112
|
-
// 포커스 및 블러 핸들러
|
|
113
|
-
const handleFocus = useCallback(() => setIsFocused(true), []);
|
|
114
|
-
const handleBlur = useCallback(() => setIsFocused(false), []);
|
|
115
|
-
|
|
116
|
-
// 상태에 따른 테두리 색상 설정
|
|
117
|
-
const computedBorderColor = useMemo(() => (
|
|
118
|
-
status === 'error' ? errorColor : isFocused ? focusColor : borderColor
|
|
119
|
-
), [status, errorColor, isFocused, focusColor, borderColor]);
|
|
120
|
-
|
|
121
|
-
// 상태에 따른 라벨 색상 설정
|
|
122
|
-
const computedLabelColor = useMemo(() => (
|
|
123
|
-
status === 'error' ? errorColor : isFocused ? focusColor : value ? labelColor : placeHolderColor
|
|
124
|
-
), [status, errorColor, isFocused, focusColor, value, placeHolderColor, labelColor]);
|
|
125
|
-
|
|
126
|
-
// 컨테이너 스타일 정의
|
|
127
|
-
const containerStyle: StyleProp<ViewStyle> = useMemo(() => ({
|
|
128
|
-
width: '100%',
|
|
129
|
-
justifyContent: isTextArea ? 'flex-start' : 'center',
|
|
130
|
-
borderRadius,
|
|
131
|
-
paddingHorizontal,
|
|
132
|
-
backgroundColor: inputBgColor,
|
|
133
|
-
paddingTop: boxStyle === 'inbox' ? 13 : 0,
|
|
134
|
-
...(boxStyle === 'outline' || boxStyle === 'inbox' ? { borderWidth } : {}),
|
|
135
|
-
...(boxStyle === 'underline' ? { borderBottomWidth: borderWidth } : {}),
|
|
136
|
-
...(innerBoxStyle === 'top' ? { borderBottomLeftRadius: 0, borderBottomRightRadius: 0, borderBottomWidth: borderWidth / 2 }
|
|
137
|
-
: innerBoxStyle === 'middle' ? { borderRadius: 0, borderTopWidth: borderWidth / 2, borderBottomWidth: borderWidth / 2 }
|
|
138
|
-
: innerBoxStyle === 'bottom' ? { borderTopLeftRadius: 0, borderTopRightRadius: 0, borderTopWidth: borderWidth / 2 }
|
|
139
|
-
: {}),
|
|
140
|
-
}), [isTextArea, borderRadius, paddingHorizontal, inputBgColor, borderWidth, boxStyle, innerBoxStyle]);
|
|
141
|
-
|
|
142
|
-
// 라벨 스타일 정의
|
|
143
|
-
const labelTextStyle: StyleProp<TextStyle> = useMemo(() => ({
|
|
144
|
-
fontSize,
|
|
145
|
-
left: paddingHorizontal,
|
|
146
|
-
backgroundColor: labelBgColor,
|
|
147
|
-
paddingHorizontal: boxStyle === 'outline' ? 5 : 0,
|
|
148
|
-
paddingVertical: 2,
|
|
149
|
-
textAlignVertical: 'center',
|
|
150
|
-
fontFamily,
|
|
151
|
-
borderRadius: boxStyle === 'outline' ? 20 : 0,
|
|
152
|
-
overflow: 'hidden',
|
|
153
|
-
}), [fontSize, paddingHorizontal, labelBgColor, boxStyle, fontFamily]);
|
|
154
|
-
|
|
155
|
-
// 텍스트 변경 핸들러
|
|
156
|
-
const handleTextChange = useCallback((text: string) => {
|
|
157
|
-
if (onChangeText) onChangeText(text);
|
|
158
|
-
}, [onChangeText]);
|
|
159
|
-
|
|
160
|
-
return (
|
|
161
|
-
<>
|
|
162
|
-
<ViewAtom
|
|
163
|
-
style={[containerStyle, { borderColor: computedBorderColor }]}
|
|
164
|
-
onLayout={handleLayout}
|
|
165
|
-
pointerEvents={disabled ? 'none' : 'auto'}
|
|
166
|
-
>
|
|
167
|
-
<TextInput
|
|
168
|
-
{...textInputProps}
|
|
169
|
-
style={[
|
|
170
|
-
{ paddingTop: 7, paddingBottom: 5 },
|
|
171
|
-
textInputProps?.style,
|
|
172
|
-
{ fontSize, width: '100%', paddingRight: 25, fontFamily },
|
|
173
|
-
]}
|
|
174
|
-
value={value}
|
|
175
|
-
onFocus={handleFocus}
|
|
176
|
-
onBlur={handleBlur}
|
|
177
|
-
onChangeText={handleTextChange}
|
|
178
|
-
allowFontScaling={allowFontScaling}
|
|
179
|
-
selectionColor={palette.grey[50]}
|
|
180
|
-
/>
|
|
181
|
-
|
|
182
|
-
<ViewAtom pointerEvents="none" style={{ position: 'absolute' }}>
|
|
183
|
-
<Animated.Text allowFontScaling={allowFontScaling} style={[animatedLabelStyle, labelTextStyle, { color: computedLabelColor }]}>
|
|
184
|
-
{label}
|
|
185
|
-
</Animated.Text>
|
|
186
|
-
</ViewAtom>
|
|
187
|
-
|
|
188
|
-
{(value && isFocused) && (
|
|
189
|
-
<ButtonClose marginTop={isTextArea ? 13 : undefined} onChangeText={onChangeText} />
|
|
190
|
-
)}
|
|
191
|
-
</ViewAtom>
|
|
192
|
-
|
|
193
|
-
{status === 'error' && errorMessage && (
|
|
194
|
-
<ErrorComponent errorMessage={errorMessage} errorColor={errorColor} fontFamily={fontFamily} />
|
|
195
|
-
)}
|
|
196
|
-
</>
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export default ZSTextField;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { TouchableOpacity } from "react-native";
|
|
2
|
-
import { SvgX } from "../../../assets/SvgX";
|
|
3
|
-
|
|
4
|
-
const ButtonClose = ({
|
|
5
|
-
onChangeText,
|
|
6
|
-
marginTop
|
|
7
|
-
}: {
|
|
8
|
-
onChangeText?: (text: string) => void;
|
|
9
|
-
marginTop?: number
|
|
10
|
-
}) => {
|
|
11
|
-
return (
|
|
12
|
-
<TouchableOpacity style={{ position: 'absolute', padding: 3, right: 15, borderRadius: 30, backgroundColor: '#e6e6e6', justifyContent: 'center', alignItems: 'center', ...marginTop && { top: marginTop } }}
|
|
13
|
-
hitSlop={{ top: 10, right: 10, bottom: 10, left: 10 }}
|
|
14
|
-
onPress={() => { onChangeText?.(''); }}>
|
|
15
|
-
<SvgX color="#5E696E" />
|
|
16
|
-
</TouchableOpacity>
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export default ButtonClose;
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Text, View } from "react-native";
|
|
2
|
-
|
|
3
|
-
const ErrorComponent = ({
|
|
4
|
-
errorMessage, errorColor, fontFamily
|
|
5
|
-
}:{
|
|
6
|
-
errorMessage: string;
|
|
7
|
-
errorColor: string;
|
|
8
|
-
fontFamily: string;
|
|
9
|
-
}) => {
|
|
10
|
-
return (
|
|
11
|
-
<View style={{ width: '100%', flexDirection: 'row', alignItems: 'center', paddingLeft: 3, marginTop: 5 }}>
|
|
12
|
-
<View style={{ width: 18, height: 18, justifyContent: 'center', alignItems: 'center', borderRadius: 30, backgroundColor: errorColor }}>
|
|
13
|
-
<Text allowFontScaling={false} style={{ fontWeight: 'bold', color: 'white', textAlign: 'center', textAlignVertical: 'center', fontSize: 11, fontFamily: fontFamily }}>
|
|
14
|
-
{`!`}
|
|
15
|
-
</Text>
|
|
16
|
-
</View>
|
|
17
|
-
|
|
18
|
-
<Text allowFontScaling={false} style={{ marginLeft: 5, fontSize: 14, color: errorColor, fontFamily: fontFamily }}>
|
|
19
|
-
{errorMessage}
|
|
20
|
-
</Text>
|
|
21
|
-
</View>
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export default ErrorComponent;
|
package/src/ui/ZSView/index.tsx
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import React, { memo, useMemo } from 'react';
|
|
2
|
-
import { ViewProps, StyleSheet } from 'react-native';
|
|
3
|
-
import { useTheme } from '../../model/useThemeProvider';
|
|
4
|
-
import AnimatedWrapper from '../atoms/AnimatedWrapper';
|
|
5
|
-
|
|
6
|
-
type Props = ViewProps & {
|
|
7
|
-
isAnimation?: boolean;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const ZSView: React.FC<Props> = ({ isAnimation = false, style, children, ...rest }) => {
|
|
11
|
-
const { palette } = useTheme();
|
|
12
|
-
|
|
13
|
-
const styles = useMemo(
|
|
14
|
-
() =>
|
|
15
|
-
StyleSheet.create({
|
|
16
|
-
container: {
|
|
17
|
-
backgroundColor: palette.background.base,
|
|
18
|
-
},
|
|
19
|
-
}),
|
|
20
|
-
[palette.background.base],
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<AnimatedWrapper isAnimation={isAnimation} style={[styles.container, style]} {...rest}>
|
|
25
|
-
{children}
|
|
26
|
-
</AnimatedWrapper>
|
|
27
|
-
);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
export default memo(ZSView);
|