@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.
Files changed (77) hide show
  1. package/build/index.d.ts +2 -1
  2. package/build/index.d.ts.map +1 -1
  3. package/build/index.js +4 -1
  4. package/build/index.js.map +1 -1
  5. package/build/model/index.d.ts +4 -0
  6. package/build/model/index.d.ts.map +1 -0
  7. package/build/model/index.js +4 -0
  8. package/build/model/index.js.map +1 -0
  9. package/build/types/index.d.ts +9 -0
  10. package/build/types/index.d.ts.map +1 -0
  11. package/build/types/index.js +2 -0
  12. package/build/types/index.js.map +1 -0
  13. package/package.json +6 -1
  14. package/.eslintrc.js +0 -5
  15. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  16. package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
  17. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  18. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  19. package/android/.gradle/8.9/gc.properties +0 -0
  20. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  21. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  22. package/android/.gradle/vcs-1/gc.properties +0 -0
  23. package/android/build.gradle +0 -43
  24. package/android/src/main/AndroidManifest.xml +0 -2
  25. package/android/src/main/java/kr/co/studio0610/zsui/ZsUiModule.kt +0 -47
  26. package/android/src/main/java/kr/co/studio0610/zsui/ZsUiView.kt +0 -7
  27. package/expo-module.config.json +0 -9
  28. package/ios/ZsUi.podspec +0 -27
  29. package/ios/ZsUiModule.swift +0 -44
  30. package/ios/ZsUiView.swift +0 -7
  31. package/src/ZsUi.types.ts +0 -7
  32. package/src/ZsUiModule.ts +0 -5
  33. package/src/ZsUiModule.web.ts +0 -13
  34. package/src/ZsUiView.tsx +0 -11
  35. package/src/ZsUiView.web.tsx +0 -11
  36. package/src/assets/SvgCheck.tsx +0 -16
  37. package/src/assets/SvgX.tsx +0 -22
  38. package/src/index.ts +0 -52
  39. package/src/model/types.ts +0 -104
  40. package/src/model/useNotify.ts +0 -14
  41. package/src/model/useNotifyProvider.tsx +0 -259
  42. package/src/model/useThemeProvider.tsx +0 -99
  43. package/src/model/utils.ts +0 -31
  44. package/src/notify/AlertNotify/index.tsx +0 -172
  45. package/src/notify/BottomSheetNotify/index.tsx +0 -177
  46. package/src/notify/BottomSheetNotify/model/useBottomSheetNotify.tsx +0 -270
  47. package/src/notify/BottomSheetNotify/types/index.ts +0 -3
  48. package/src/notify/BottomSheetNotify/ui/BSTextInput/index.tsx +0 -28
  49. package/src/notify/BottomSheetNotify/ui/ContentsComponent/index.tsx +0 -86
  50. package/src/notify/LoadingNotify/index.tsx +0 -46
  51. package/src/notify/PopOver/PopOverButton.tsx +0 -56
  52. package/src/notify/PopOver/PopOverMenu.tsx +0 -95
  53. package/src/notify/SnackbarNotify/index.tsx +0 -43
  54. package/src/notify/SnackbarNotify/ui/SnackbarItem.tsx +0 -103
  55. package/src/notify/index.ts +0 -21
  56. package/src/notify/ui/ModalBackground.tsx +0 -46
  57. package/src/theme/index.ts +0 -3
  58. package/src/theme/palette.ts +0 -384
  59. package/src/theme/types.ts +0 -170
  60. package/src/theme/typography.ts +0 -199
  61. package/src/ui/ThrottleButton/index.tsx +0 -119
  62. package/src/ui/ZSBottomButton/index.tsx +0 -151
  63. package/src/ui/ZSContainer/index.tsx +0 -92
  64. package/src/ui/ZSPressable/index.tsx +0 -82
  65. package/src/ui/ZSRadioGroup/index.tsx +0 -141
  66. package/src/ui/ZSText/index.tsx +0 -22
  67. package/src/ui/ZSTextField/index.tsx +0 -200
  68. package/src/ui/ZSTextField/ui/ButtonClose.tsx +0 -20
  69. package/src/ui/ZSTextField/ui/ErrorComponent.tsx +0 -25
  70. package/src/ui/ZSView/index.tsx +0 -30
  71. package/src/ui/atoms/AnimatedWrapper.tsx +0 -90
  72. package/src/ui/atoms/ScrollViewAtom.tsx +0 -17
  73. package/src/ui/atoms/TextAtom.tsx +0 -15
  74. package/src/ui/atoms/ViewAtom.tsx +0 -15
  75. package/src/ui/index.ts +0 -27
  76. package/src/ui/types.ts +0 -12
  77. package/tsconfig.json +0 -9
@@ -1,177 +0,0 @@
1
- import React, { forwardRef, useImperativeHandle, useMemo } from 'react';
2
- import { StyleSheet, Dimensions, ViewProps, Keyboard } from 'react-native';
3
- import { GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler';
4
- import Animated, { FadeIn, FadeOut } from 'react-native-reanimated';
5
- import useBottomSheetNotify from './model/useBottomSheetNotify';
6
- import { BottomSheetNotifyRef } from './types';
7
- import ContentsComponent from './ui/ContentsComponent';
8
- import { useTheme } from '../../model/useThemeProvider';
9
- import { ThemeBackground } from '../../theme';
10
- import ViewAtom from '../../ui/atoms/ViewAtom';
11
- import { ZSView } from '../../ui';
12
-
13
- const DEFAULT_BORDER_RADIUS = 24;
14
- const BS_MAX_HEIGHT = Dimensions.get('window').height - 120;
15
-
16
- interface Props extends ViewProps {
17
- marginBottomBS?: number;
18
- bottomSheetBackgroundColor?: string;
19
- bottomSheetPadding?: number;
20
- closeOffset?: number;
21
- contentsGestureEnable?: boolean;
22
- isHandleVisible?: boolean;
23
- bottomSheetMarginX?: number;
24
- isBottomRadius?: boolean;
25
- maxHeight?: number;
26
- isScrollView?: boolean;
27
- bottomSheetComponent: React.ReactNode;
28
- showsVerticalScrollIndicator: boolean;
29
- headerComponent?: React.ReactNode;
30
- }
31
-
32
- function BottomSheetNotify(props: Props, ref: React.Ref<BottomSheetNotifyRef>) {
33
- const {
34
- marginBottomBS = 15,
35
- bottomSheetPadding = 20,
36
- bottomSheetBackgroundColor,
37
- closeOffset = Dimensions.get('window').height,
38
- contentsGestureEnable = true,
39
- isHandleVisible = true,
40
- bottomSheetMarginX = 10,
41
- isBottomRadius = true,
42
- isScrollView = true,
43
- maxHeight = BS_MAX_HEIGHT,
44
- bottomSheetComponent,
45
- showsVerticalScrollIndicator,
46
- headerComponent
47
- } = props;
48
-
49
- const {
50
- HANDLE_HEIGHT,
51
- bottomSheetVisible,
52
- bsAnimatedStyle,
53
- onGestureEvent,
54
- handleVisible,
55
- onTapEvent,
56
- openPosition,
57
- screenWidth,
58
- screenHeight,
59
- panGestureRef,
60
- listScrollPosition,
61
- bsModalBgStyle,
62
- backgroundPressHandler
63
- } = useBottomSheetNotify({
64
- bottomSheetPadding,
65
- closeOffset,
66
- contentsGestureEnable,
67
- bottomSheetMarginX,
68
- isHandleVisible,
69
- });
70
-
71
- const { palette: { background } } = useTheme();
72
-
73
- const styles = useMemo(
74
- () => createStyles({ background }),
75
- [background]
76
- );
77
-
78
- useImperativeHandle(ref, () => ({
79
- handleVisible,
80
- }));
81
-
82
- return bottomSheetVisible && bottomSheetComponent ? (
83
- <Animated.View
84
- style={[styles.modalBg, bsModalBgStyle]}
85
- entering={FadeIn.duration(50)}
86
- exiting={FadeOut.duration(50)}
87
- onTouchEnd={backgroundPressHandler} // 외부 터치 시 시트 닫기
88
- >
89
- <GestureHandlerRootView style={styles.rootViewWrapper}>
90
- <GestureDetector gesture={onGestureEvent}>
91
- <Animated.View
92
- onTouchEnd={(e) => {
93
- e.stopPropagation();
94
- Keyboard.dismiss(); // 키보드 숨김
95
- }}
96
- style={[
97
- styles.sheet,
98
- {
99
- width: screenWidth,
100
- height: screenHeight,
101
- paddingHorizontal: bottomSheetPadding,
102
- left: bottomSheetMarginX,
103
- right: bottomSheetMarginX,
104
- borderTopLeftRadius: DEFAULT_BORDER_RADIUS,
105
- borderTopRightRadius: DEFAULT_BORDER_RADIUS,
106
- borderBottomLeftRadius: isBottomRadius ? DEFAULT_BORDER_RADIUS : 0,
107
- borderBottomRightRadius: isBottomRadius ? DEFAULT_BORDER_RADIUS : 0,
108
- backgroundColor: bottomSheetBackgroundColor || background.base,
109
- },
110
- bsAnimatedStyle, // 애니메이션 스타일 적용
111
- ]}
112
- >
113
- {isHandleVisible && (
114
- <ZSView style={[styles.handleContainer, { height: HANDLE_HEIGHT }]}>
115
- <ViewAtom style={styles.handle} />
116
- </ZSView>
117
- )}
118
-
119
- <GestureDetector gesture={onTapEvent}>
120
- <ContentsComponent
121
- HANDLE_HEIGHT={HANDLE_HEIGHT}
122
- panGestureRef={panGestureRef}
123
- listScrollPosition={listScrollPosition}
124
- openPosition={openPosition}
125
- marginBottomBS={marginBottomBS}
126
- screenHeight={screenHeight}
127
- bottomSheetComponent={bottomSheetComponent}
128
- bottomSheetPadding={bottomSheetPadding}
129
- maxHeight={maxHeight}
130
- isScrollView={isScrollView}
131
- showsVerticalScrollIndicator={showsVerticalScrollIndicator}
132
- headerComponent={headerComponent}
133
- />
134
- </GestureDetector>
135
- </Animated.View>
136
- </GestureDetector>
137
- </GestureHandlerRootView>
138
- </Animated.View>
139
- ) : null;
140
- }
141
-
142
-
143
- const createStyles = ({
144
- background,
145
- }: {
146
- background: ThemeBackground;
147
- }) =>
148
- StyleSheet.create({
149
- modalBg: {
150
- position: 'absolute',
151
- width: Dimensions.get('window').width,
152
- height: Dimensions.get('window').height,
153
- bottom: 0,
154
- },
155
- sheet: {
156
- position: 'absolute',
157
- zIndex: 9000,
158
- overflow: 'hidden',
159
- },
160
- handleContainer: {
161
- width: '100%',
162
- alignItems: 'center',
163
- paddingTop: 13,
164
- },
165
- handle: {
166
- backgroundColor: background.layer2,
167
- width: 50,
168
- height: 4,
169
- borderRadius: 2,
170
- },
171
- rootViewWrapper: {
172
- width: '100%',
173
- height: '100%',
174
- },
175
- });
176
-
177
- export default forwardRef(BottomSheetNotify);
@@ -1,270 +0,0 @@
1
- import { useCallback, useEffect, useRef, useState } from 'react';
2
- import { BackHandler, Dimensions, Keyboard, Platform, useWindowDimensions } from 'react-native';
3
- import { Gesture, GestureType } from 'react-native-gesture-handler';
4
- import { useSharedValue, useAnimatedStyle, withTiming, Easing, runOnJS, useDerivedValue } from 'react-native-reanimated';
5
-
6
- const DIMENSIONS_HEIGHT = Dimensions.get('window').height;
7
- const INPUT_HEIGHT_CORRECTION = 40; // 인풋 높이 보정
8
- const NATURAL_GESTURE_TOP = -17; // 자연스러운 제스쳐를 위해 상단 여유 공간 추가
9
- const NATURAL_GESTURE_X = 8;
10
- const DEFAULT_BG_OPACITY = 40;
11
- const HANDLE_HEIGHT = 35;
12
-
13
- const timingConfig100 = {
14
- duration: 100,
15
- easing: Easing.inOut(Easing.quad),
16
- };
17
-
18
- const timingConfig200 = {
19
- duration: 200,
20
- easing: Easing.inOut(Easing.quad),
21
- };
22
-
23
- interface Props {
24
- bottomSheetPadding: number;
25
- closeOffset: number;
26
- contentsGestureEnable: boolean;
27
- isHandleVisible: boolean;
28
- bottomSheetMarginX: number;
29
- }
30
-
31
- function useBottomSheetNotify({
32
- bottomSheetPadding,
33
- closeOffset,
34
- contentsGestureEnable,
35
- bottomSheetMarginX,
36
- isHandleVisible,
37
- }: Props) {
38
- const handleHeight = isHandleVisible ? HANDLE_HEIGHT : 0;
39
- const { width: windowWidth } = useWindowDimensions();
40
- const panGestureRef = useRef<GestureType>(Gesture.Pan());
41
- const listScrollPosition = useSharedValue(0);
42
- const gestureComponent = useSharedValue('');
43
- const tabAbsoluteY = useSharedValue(0);
44
- const screenWidth = useSharedValue(Dimensions.get('window').width);
45
- const screenHeight = useSharedValue(1);
46
- const openPosition = useSharedValue(0);
47
- const bsScale = useSharedValue(1);
48
- const bgOpacity = useSharedValue(DEFAULT_BG_OPACITY);
49
- const translateX = useSharedValue(0);
50
- const translateY = useSharedValue(closeOffset);
51
- const fullScreen = useSharedValue(false);
52
- const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);
53
- const [bottomSheetVisible, setBottomSheetVisible] = useState(false);
54
-
55
- // ** 바텀시트 백그라운드 애니메이션 스타일 정의
56
- const bsModalBgStyle = useAnimatedStyle(() => ({
57
- backgroundColor: `#1E1E1E${bgOpacity.value}`,
58
- }));
59
-
60
- // ** 바텀시트 애니메이션 스타일 정의
61
- const bsAnimatedStyle = useAnimatedStyle(() => ({
62
- transform: [
63
- { translateX: translateX.value },
64
- { translateY: translateY.value },
65
- { scale: bsScale.value },
66
- ],
67
- }));
68
-
69
- // ** 화면 너비 설정
70
- useEffect(() => {
71
- screenWidth.value = windowWidth - (bottomSheetMarginX ? bottomSheetMarginX * 2 : 0);
72
- }, [windowWidth, bottomSheetMarginX]);
73
-
74
- // ** 바텀시트 초기화
75
- const initBottomSheet = useCallback(() => {
76
- screenHeight.value = 1;
77
- openPosition.value = 0;
78
- }, [screenHeight, openPosition]);
79
-
80
- // ** 백버튼 핸들러 정의 (안드로이드 전용)
81
- const backPressHandler = useCallback(() => {
82
- if (bottomSheetVisible) {
83
- setBottomSheetVisible(false);
84
- return true;
85
- }
86
- return false;
87
- }, [bottomSheetVisible]);
88
-
89
- // ** 백버튼 이벤트 리스너 설정
90
- useEffect(() => {
91
- const backHandler = BackHandler.addEventListener('hardwareBackPress', backPressHandler);
92
-
93
- return () => backHandler.remove();
94
- }, [backPressHandler]);
95
-
96
- // ** 바텀시트 보이기 상태 감지 후 초기화
97
- useEffect(() => {
98
- if (!bottomSheetVisible) initBottomSheet();
99
- }, [bottomSheetVisible, initBottomSheet]);
100
-
101
- // ** 백그라운드 클릭시 키보드나 바텀시트 닫기
102
- const backgroundPressHandler = useCallback(() => {
103
- if (isKeyboardVisible) {
104
- Keyboard.dismiss();
105
- } else {
106
- handleVisible(false);
107
- }
108
- }, [isKeyboardVisible]);
109
-
110
- // ** 키보드 닫기
111
- const dismissKeyboard = useCallback(() => {
112
- Keyboard.dismiss();
113
- }, []);
114
-
115
- // ** 애니메이션을 사용한 바텀시트 초기 크기 설정
116
- const initSize = useCallback(() => {
117
- bsScale.value = withTiming(1, timingConfig100);
118
- translateX.value = withTiming(0, timingConfig100);
119
- bgOpacity.value = DEFAULT_BG_OPACITY;
120
- }, [bsScale, translateX, bgOpacity]);
121
-
122
- // ** 소프트 키보드 핸들링 (보이기, 숨기기)
123
- useEffect(() => {
124
- const keyboardDidShowListener = Keyboard.addListener(
125
- Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow',
126
- (event) => {
127
- setIsKeyboardVisible(true);
128
- if (!fullScreen.value) return;
129
- const tabAbsoluteYValue = tabAbsoluteY.value;
130
- const screenTopToTarget = tabAbsoluteYValue - openPosition.value; // 모달 상단에서 인풋까지 거리
131
- const keyboardHeight = event.endCoordinates.height; // 키보드 높이
132
- const keyboardLine = DIMENSIONS_HEIGHT - keyboardHeight;
133
-
134
- translateY.value = withTiming(keyboardLine - screenTopToTarget - INPUT_HEIGHT_CORRECTION, timingConfig200);
135
- }
136
- );
137
-
138
- const keyboardDidHideListener = Keyboard.addListener(
139
- Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide',
140
- () => {
141
- setIsKeyboardVisible(false);
142
-
143
- // 키보드가 사라질 때 화면의 높이를 원래대로 돌립니다.
144
- if (!fullScreen.value) return;
145
- handleVisible(true);
146
- }
147
- );
148
-
149
- return () => {
150
- keyboardDidShowListener.remove();
151
- keyboardDidHideListener.remove();
152
- };
153
- }, [fullScreen, openPosition, tabAbsoluteY, translateY]);
154
-
155
- // ** 바텀시트 열기 함수
156
- const openBottomSheet = useCallback(() => {
157
- setTimeout(() => {
158
- if (screenHeight.value === 1) return openBottomSheet();
159
- translateY.value = withTiming(openPosition.value, timingConfig200);
160
- fullScreen.value = true;
161
- }, 200);
162
- }, [screenHeight, openPosition, translateY]);
163
-
164
- // ** 바텀시트 위치 변경
165
- const onOpenPositionChange = useCallback(
166
- (value: number) => {
167
- if (fullScreen.value && screenHeight.value !== 1) {
168
- translateY.value = withTiming(value, timingConfig200);
169
- }
170
- },
171
- [fullScreen, screenHeight, translateY]
172
- );
173
-
174
- // ** 애니메이션 값이 변경될 때 처리
175
- useDerivedValue(() => {
176
- runOnJS(onOpenPositionChange)(openPosition.value);
177
- return openPosition.value;
178
- }, [openPosition]);
179
-
180
- // ** 바텀시트 상태 관리 (열기/닫기)
181
- const handleVisible = useCallback(
182
- (isOpen: boolean) => {
183
- if (isOpen) {
184
- setBottomSheetVisible(true);
185
- openBottomSheet();
186
- } else {
187
- translateY.value = withTiming(closeOffset, timingConfig200);
188
- fullScreen.value = false;
189
- setTimeout(() => {
190
- setBottomSheetVisible(false);
191
- }, 200);
192
- }
193
- },
194
- [closeOffset, fullScreen, openBottomSheet, translateY]
195
- );
196
-
197
- // ** 탭 제스처 설정
198
- const onTapEvent = Gesture.Tap().onStart((event) => {
199
- tabAbsoluteY.value = event.absoluteY;
200
- });
201
-
202
- // ** 팬 제스처 설정
203
- const onGestureEvent = Gesture.Pan()
204
- .onStart((event) => {
205
- 'worklet';
206
- runOnJS(dismissKeyboard)();
207
-
208
- // 제스쳐 영역 판단
209
- if (openPosition.value + handleHeight > event.absoluteY) {
210
- gestureComponent.value = 'Handler';
211
- bsScale.value = withTiming(0.98, timingConfig100);
212
- } else {
213
- gestureComponent.value = 'Contents';
214
- if (contentsGestureEnable) bsScale.value = withTiming(0.98, timingConfig100);
215
- }
216
- })
217
- .onUpdate((event) => {
218
- 'worklet';
219
- // 제스처 제어 로직
220
- if (!contentsGestureEnable && gestureComponent.value === 'Contents') return;
221
-
222
- const translateXValue = event.translationX;
223
- const translateYValue = event.translationY;
224
- const calcBg = Math.round(DEFAULT_BG_OPACITY + translateYValue * -1);
225
-
226
- // 백그라운드 컬러 업데이트
227
- if (calcBg < 70 && calcBg > DEFAULT_BG_OPACITY) {
228
- bgOpacity.value = calcBg;
229
- }
230
-
231
- // 자연스러운 X축 움직임
232
- if (NATURAL_GESTURE_X > translateXValue && translateXValue > -NATURAL_GESTURE_X && bsScale.value !== 1) {
233
- translateX.value = translateXValue;
234
- }
235
-
236
- // 상단 제스처 제한
237
- if (fullScreen.value && translateYValue < NATURAL_GESTURE_TOP) return;
238
- if (!fullScreen.value && translateYValue > 0 && translateY.value === closeOffset) return;
239
-
240
- const result = translateYValue + (fullScreen.value ? openPosition.value : 30);
241
- translateY.value = result;
242
- })
243
- .onEnd(() => {
244
- 'worklet';
245
- runOnJS(initSize)();
246
- const shouldOpen = translateY.value < openPosition.value + screenHeight.value / 2;
247
- runOnJS(handleVisible)(shouldOpen);
248
- })
249
- .withRef(panGestureRef);
250
-
251
- return {
252
- HANDLE_HEIGHT,
253
- bottomSheetVisible,
254
- bsAnimatedStyle,
255
- onGestureEvent,
256
- handleVisible,
257
- screenWidth,
258
- screenHeight,
259
- handleHeight,
260
- openPosition,
261
- bottomSheetPadding,
262
- onTapEvent,
263
- panGestureRef,
264
- listScrollPosition,
265
- bsModalBgStyle,
266
- backgroundPressHandler,
267
- };
268
- }
269
-
270
- export default useBottomSheetNotify;
@@ -1,3 +0,0 @@
1
- export interface BottomSheetNotifyRef {
2
- handleVisible: (isOpen: boolean) => void;
3
- }
@@ -1,28 +0,0 @@
1
- import { useRef } from "react";
2
- import { Platform, Pressable } from "react-native";
3
- import { TextInput } from "react-native-gesture-handler";
4
-
5
- type Props = {
6
- };
7
-
8
- const BSTextInput = ({ ...props }: Props) => {
9
- const textInputRef = useRef<TextInput>(null);
10
-
11
- return (
12
- Platform.OS === 'ios' ?
13
- <Pressable onPress={() => { textInputRef?.current?.focus(); }}>
14
- <TextInput
15
- ref={textInputRef}
16
- pointerEvents='none'
17
- {...props}
18
- />
19
- </Pressable>
20
- :
21
- <TextInput
22
- ref={textInputRef}
23
- {...props}
24
- />
25
- );
26
- };
27
-
28
- export default BSTextInput;
@@ -1,86 +0,0 @@
1
- import React, { useCallback } from 'react';
2
- import { Dimensions, LayoutChangeEvent, NativeScrollEvent, NativeSyntheticEvent, Text, View } from 'react-native';
3
- import { GestureType, ScrollView } from 'react-native-gesture-handler';
4
- import { SharedValue } from 'react-native-reanimated';
5
- import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
- import { ZSView } from '../../../../ui';
7
-
8
- // const ANDROID_STATUS_BAR_HEIGHT = Platform.OS === 'android' ? 25 : 0;
9
- const ANDROID_STATUS_BAR_HEIGHT = 0;
10
-
11
- interface Props {
12
- HANDLE_HEIGHT: number;
13
- panGestureRef: React.MutableRefObject<GestureType>;
14
- listScrollPosition: SharedValue<number>;
15
- openPosition: SharedValue<number>;
16
- marginBottomBS: number;
17
- screenHeight: SharedValue<number>;
18
- bottomSheetComponent: React.ReactNode;
19
- bottomSheetPadding: number;
20
- maxHeight: number;
21
- isScrollView: boolean;
22
- showsVerticalScrollIndicator: boolean;
23
- headerComponent?: React.ReactNode;
24
- }
25
-
26
- // 화살표 함수 대신 일반 함수 사용
27
- function ContentsComponent({
28
- HANDLE_HEIGHT,
29
- panGestureRef,
30
- listScrollPosition,
31
- openPosition,
32
- marginBottomBS,
33
- screenHeight,
34
- bottomSheetComponent,
35
- bottomSheetPadding,
36
- maxHeight,
37
- isScrollView,
38
- showsVerticalScrollIndicator,
39
- headerComponent
40
- }: Props) {
41
- const { bottom } = useSafeAreaInsets();
42
-
43
- // onLayout 함수를 useCallback으로 최적화
44
- const onLayout = useCallback((event: LayoutChangeEvent) => {
45
- const { height } = event.nativeEvent.layout;
46
- const contentMaxHeight = maxHeight + HANDLE_HEIGHT;
47
- const resultHeight = Math.min(height, contentMaxHeight); // 더 간결하게 변경
48
- // 성능 문제 방지를 위해 runOnUI 사용
49
- screenHeight.value = resultHeight + HANDLE_HEIGHT;
50
- openPosition.value = Dimensions.get('window').height - resultHeight - marginBottomBS - bottom - ANDROID_STATUS_BAR_HEIGHT - HANDLE_HEIGHT;
51
- }, [maxHeight, HANDLE_HEIGHT, screenHeight, openPosition, marginBottomBS, bottom]);
52
-
53
- // 현재 스크롤 위치를 관리하는 함수
54
- const handleScroll = useCallback((event: NativeSyntheticEvent<NativeScrollEvent>) => {
55
- listScrollPosition.value = event.nativeEvent.contentOffset.y;
56
- }, [listScrollPosition]);
57
-
58
- return isScrollView ? (
59
- <ScrollView
60
- simultaneousHandlers={[panGestureRef]}
61
- onScroll={handleScroll}
62
- style={{ maxHeight }}
63
- keyboardShouldPersistTaps="handled"
64
- bounces={false}
65
- bouncesZoom={false}
66
- overScrollMode="never"
67
- showsVerticalScrollIndicator={showsVerticalScrollIndicator}
68
- scrollEventThrottle={16}
69
- stickyHeaderIndices={headerComponent ? [0] : undefined}
70
- >
71
- {headerComponent && headerComponent}
72
-
73
- <ZSView style={{ width: '100%', minHeight: 1, paddingBottom: bottomSheetPadding }} onLayout={onLayout}>
74
- {bottomSheetComponent}
75
- </ZSView>
76
- </ScrollView>
77
- ) : (
78
- <ZSView style={{ width: '100%', minHeight: 1, paddingBottom: bottomSheetPadding, maxHeight }} onLayout={onLayout}>
79
- {headerComponent && headerComponent}
80
-
81
- {bottomSheetComponent}
82
- </ZSView>
83
- );
84
- }
85
-
86
- export default React.memo(ContentsComponent); // React.memo로 성능 최적화
@@ -1,46 +0,0 @@
1
- import { ActivityIndicator, BackHandler } from "react-native";
2
- import React, { ReactNode, useEffect, useCallback } from "react";
3
- import { useNotify } from "../../model/useNotify";
4
- import ModalBackground from "../ui/ModalBackground";
5
-
6
- // 함수 선언식으로 변경
7
- function LoadingNotify({
8
- loaderComponent,
9
- }: {
10
- loaderComponent?: () => ReactNode;
11
- }) {
12
- const { loaderVisible } = useNotify();
13
-
14
- // BackHandler 이벤트 처리 최적화
15
- useEffect(() => {
16
- const handleBackPressed = () => {
17
- if (loaderVisible) return true; // 로더가 보이는 경우 뒤로가기 방지
18
- return false;
19
- };
20
-
21
- const handler = BackHandler.addEventListener(
22
- "hardwareBackPress",
23
- handleBackPressed
24
- );
25
-
26
- // cleanup 함수 추가
27
- return () => handler.remove();
28
- }, [loaderVisible]);
29
-
30
- // loaderComponent를 메모이제이션
31
- const renderLoader = useCallback(() => {
32
- return loaderComponent ? (
33
- loaderComponent()
34
- ) : (
35
- <ActivityIndicator size="large" color="#fff" />
36
- );
37
- }, [loaderComponent]);
38
-
39
- return loaderVisible ? (
40
- <ModalBackground>
41
- {renderLoader()}
42
- </ModalBackground>
43
- ) : null;
44
- }
45
-
46
- export default LoadingNotify;
@@ -1,56 +0,0 @@
1
- import React, { useRef, useCallback } from 'react';
2
- import { View, Pressable, ViewProps, StyleSheet } from 'react-native';
3
- import { useNotify } from '../../model/useNotify';
4
-
5
- interface PopOverButtonProps extends ViewProps {
6
- width: number;
7
- height: number;
8
- backgroundColor?: string;
9
- popOverMenuComponent: React.ReactNode;
10
- }
11
-
12
- const PopOverButton: React.FC<PopOverButtonProps> = ({
13
- width,
14
- height,
15
- backgroundColor = 'transparent',
16
- popOverMenuComponent,
17
- children,
18
- ...props
19
- }) => {
20
- const buttonRef = useRef<View>(null);
21
- const { showPopOverMenu } = useNotify();
22
-
23
- const handlePress = useCallback(() => {
24
- buttonRef.current?.measure((fx, fy, measuredWidth, measuredHeight, pageX, pageY) => {
25
- if (pageX !== undefined && pageY !== undefined) {
26
- const rbX = pageX + measuredWidth;
27
- const rbY = pageY + measuredHeight;
28
-
29
- showPopOverMenu({ px: rbX, py: rbY, component: popOverMenuComponent });
30
- }
31
- });
32
- }, [showPopOverMenu, popOverMenuComponent]);
33
-
34
- return (
35
- <Pressable onPress={handlePress} style={styles.pressable}>
36
- <View
37
- ref={buttonRef}
38
- style={[styles.button, { width, height, backgroundColor }]}
39
- {...props}
40
- >
41
- {children}
42
- </View>
43
- </Pressable>
44
- );
45
- };
46
-
47
- const styles = StyleSheet.create({
48
- pressable: {
49
- alignItems: 'flex-start',
50
- },
51
- button: {
52
- justifyContent: 'center',
53
- },
54
- });
55
-
56
- export default PopOverButton;