@0610studio/zs-ui 0.7.1 → 0.7.3

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 (55) hide show
  1. package/build/index.d.ts +5 -4
  2. package/build/index.d.ts.map +1 -1
  3. package/build/index.js +4 -3
  4. package/build/index.js.map +1 -1
  5. package/build/model/types.d.ts +4 -10
  6. package/build/model/types.d.ts.map +1 -1
  7. package/build/model/types.js.map +1 -1
  8. package/build/model/useOverlay.d.ts +1 -3
  9. package/build/model/useOverlay.d.ts.map +1 -1
  10. package/build/model/useOverlay.js +0 -8
  11. package/build/model/useOverlay.js.map +1 -1
  12. package/build/model/useOverlayProvider.d.ts.map +1 -1
  13. package/build/model/useOverlayProvider.js +11 -38
  14. package/build/model/useOverlayProvider.js.map +1 -1
  15. package/build/model/utils.d.ts.map +1 -1
  16. package/build/model/utils.js.map +1 -1
  17. package/build/overlay/BottomSheetOverlay/index.d.ts +2 -1
  18. package/build/overlay/BottomSheetOverlay/index.d.ts.map +1 -1
  19. package/build/overlay/BottomSheetOverlay/index.js +144 -91
  20. package/build/overlay/BottomSheetOverlay/index.js.map +1 -1
  21. package/build/overlay/ZSPortal/index.d.ts +11 -0
  22. package/build/overlay/ZSPortal/index.d.ts.map +1 -0
  23. package/build/overlay/ZSPortal/index.js +49 -0
  24. package/build/overlay/ZSPortal/index.js.map +1 -0
  25. package/build/overlay/index.d.ts +2 -1
  26. package/build/overlay/index.d.ts.map +1 -1
  27. package/build/overlay/index.js +2 -1
  28. package/build/overlay/index.js.map +1 -1
  29. package/build/ui/ZSAboveKeyboard/index.d.ts +9 -0
  30. package/build/ui/ZSAboveKeyboard/index.d.ts.map +1 -0
  31. package/build/ui/ZSAboveKeyboard/index.js +56 -0
  32. package/build/ui/ZSAboveKeyboard/index.js.map +1 -0
  33. package/build/ui/ZSContainer/index.d.ts +16 -14
  34. package/build/ui/ZSContainer/index.d.ts.map +1 -1
  35. package/build/ui/ZSContainer/index.js +78 -40
  36. package/build/ui/ZSContainer/index.js.map +1 -1
  37. package/build/ui/ZSPressable/index.d.ts +2 -1
  38. package/build/ui/ZSPressable/index.d.ts.map +1 -1
  39. package/build/ui/ZSPressable/index.js +40 -16
  40. package/build/ui/ZSPressable/index.js.map +1 -1
  41. package/build/ui/ZSTextField/index.d.ts.map +1 -1
  42. package/build/ui/ZSTextField/index.js +127 -41
  43. package/build/ui/ZSTextField/index.js.map +1 -1
  44. package/build/ui/atoms/AnimatedWrapper.d.ts.map +1 -1
  45. package/build/ui/atoms/AnimatedWrapper.js +58 -16
  46. package/build/ui/atoms/AnimatedWrapper.js.map +1 -1
  47. package/package.json +1 -1
  48. package/build/overlay/AboveKeyboard/index.d.ts +0 -4
  49. package/build/overlay/AboveKeyboard/index.d.ts.map +0 -1
  50. package/build/overlay/AboveKeyboard/index.js +0 -41
  51. package/build/overlay/AboveKeyboard/index.js.map +0 -1
  52. package/build/ui/ZSContainer/ui/VariantView.d.ts +0 -17
  53. package/build/ui/ZSContainer/ui/VariantView.d.ts.map +0 -1
  54. package/build/ui/ZSContainer/ui/VariantView.js +0 -13
  55. package/build/ui/ZSContainer/ui/VariantView.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState, useRef } from 'react';
1
+ import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';
2
2
  import { StyleSheet, View, PanResponder, Keyboard, Platform } from 'react-native';
3
3
  import { useBottomSheet } from '../../model/useOverlay';
4
4
  import Animated, { useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';
@@ -6,53 +6,82 @@ import ModalBackground from '../ui/ModalBackground';
6
6
  import { useTheme } from '../../model';
7
7
  import { useSafeAreaInsets, initialWindowMetrics } from 'react-native-safe-area-context';
8
8
  import { MAX_OVERLAY_WIDTH, Z_INDEX_VALUE } from '../../model/utils';
9
+ const IS_IOS = Platform.OS === 'ios';
10
+ const keyboardEvents = ({
11
+ showEvent: IS_IOS ? 'keyboardWillShow' : 'keyboardDidShow',
12
+ hideEvent: IS_IOS ? 'keyboardWillHide' : 'keyboardDidHide',
13
+ });
14
+ const ANIMATION_CONFIG = {
15
+ keyboard: {
16
+ show: { duration: IS_IOS ? 250 : 300 },
17
+ hide: { duration: IS_IOS ? 150 : 200 },
18
+ },
19
+ spring: {
20
+ damping: 50,
21
+ stiffness: 300,
22
+ mass: 0.7,
23
+ velocity: 100,
24
+ restDisplacementThreshold: 0.2,
25
+ },
26
+ close: { duration: 150 },
27
+ scale: { duration: 200 },
28
+ scaleRestore: {
29
+ damping: 15,
30
+ stiffness: 300,
31
+ },
32
+ };
33
+ const GESTURE_CONSTANTS = {
34
+ scaleAmount: 0.985,
35
+ horizontalDamping: 18,
36
+ verticalUpDamping: 18,
37
+ verticalDownDamping: 1.5,
38
+ closeVelocityThreshold: 0.5,
39
+ closeDistanceRatio: 1 / 3,
40
+ hideDelay: 200,
41
+ };
9
42
  function BottomSheetOverlay({ headerComponent, component, options = {}, }) {
10
43
  const { isBackgroundTouchClose = true, marginHorizontal = 10, marginBottom = 10, padding = 14, } = options;
11
44
  const { palette, dimensions: { width: windowWidth, height: windowHeight } } = useTheme();
12
45
  const { bottomSheetVisible, setBottomSheetVisible, height } = useBottomSheet();
13
- // 화면의 크기보다 높이가 높으면 화면의 크기로 제한
14
- const maxHeight = Math.min((windowHeight - 30 - (initialWindowMetrics?.insets.bottom || 0) - (initialWindowMetrics?.insets.top || 0)), height);
15
- const translateY = useSharedValue(maxHeight);
16
- const translateX = useSharedValue(0);
17
- const scale = useSharedValue(1);
18
46
  const { bottom } = useSafeAreaInsets();
47
+ // 화면의 크기보다 높이가 높으면 화면의 크기로 제한
48
+ const maxHeight = useMemo(() => Math.min(windowHeight - 30 - (initialWindowMetrics?.insets.bottom || 0) - (initialWindowMetrics?.insets.top || 0), height), [windowHeight, height]);
49
+ const translateY = useRef(useSharedValue(0)).current;
50
+ const translateX = useRef(useSharedValue(0)).current;
51
+ const scale = useRef(useSharedValue(1)).current;
19
52
  const startX = useRef(0);
20
53
  const startY = useRef(0);
21
54
  const [localVisible, setLocalVisible] = useState(false);
22
- // ** 소프트 키보드 핸들링
55
+ const handleKeyboardShow = useCallback((event) => {
56
+ const targetY = IS_IOS ? (-event.endCoordinates.height + bottom) : 0;
57
+ translateY.value = withTiming(targetY, ANIMATION_CONFIG.keyboard.show);
58
+ }, [translateY, bottom]);
59
+ const handleKeyboardHide = useCallback(() => {
60
+ translateY.value = withTiming(0, ANIMATION_CONFIG.keyboard.hide);
61
+ }, [translateY]);
62
+ // 소프트 키보드 핸들링
23
63
  useEffect(() => {
24
- const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
25
- const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
26
- const keyboardShowSubscription = Keyboard.addListener(showEvent, (event) => {
27
- translateY.value = withTiming(Platform.OS === 'ios' ? (-event.endCoordinates.height + bottom) : 0, { duration: 300 });
28
- });
29
- const keyboardHideSubscription = Keyboard.addListener(hideEvent, () => {
30
- translateY.value = withTiming(0, { duration: 200 });
31
- });
64
+ const keyboardShowSubscription = Keyboard.addListener(keyboardEvents.showEvent, handleKeyboardShow);
65
+ const keyboardHideSubscription = Keyboard.addListener(keyboardEvents.hideEvent, handleKeyboardHide);
32
66
  return () => {
33
67
  keyboardShowSubscription.remove();
34
68
  keyboardHideSubscription.remove();
35
69
  };
36
- }, []);
70
+ }, [keyboardEvents.showEvent, keyboardEvents.hideEvent, handleKeyboardShow, handleKeyboardHide]);
71
+ // BottomSheet 표시/숨김 애니메이션 처리
37
72
  useEffect(() => {
38
73
  if (bottomSheetVisible) {
39
74
  Keyboard.dismiss();
40
75
  setLocalVisible(true);
41
- translateY.value = withSpring(0, {
42
- damping: 50,
43
- stiffness: 300,
44
- mass: 0.7,
45
- velocity: 100,
46
- restDisplacementThreshold: 0.2,
47
- });
76
+ translateY.value = withSpring(0, ANIMATION_CONFIG.spring);
48
77
  }
49
78
  else {
50
- translateY.value = withTiming(maxHeight + 100, { duration: 150 });
79
+ translateY.value = withTiming(maxHeight + 100, ANIMATION_CONFIG.close);
51
80
  setTimeout(() => {
52
81
  setLocalVisible(false);
53
- }, 200);
82
+ }, GESTURE_CONSTANTS.hideDelay);
54
83
  }
55
- }, [bottomSheetVisible]);
84
+ }, [bottomSheetVisible, translateY, maxHeight]);
56
85
  const animatedStyles = useAnimatedStyle(() => {
57
86
  return {
58
87
  transform: [
@@ -61,75 +90,91 @@ function BottomSheetOverlay({ headerComponent, component, options = {}, }) {
61
90
  { scale: scale.value }
62
91
  ],
63
92
  };
64
- });
65
- const panResponder = useRef(PanResponder.create({
93
+ }, []);
94
+ const handlePanResponderGrant = useCallback(() => {
95
+ Keyboard.dismiss();
96
+ startX.current = translateX.value;
97
+ startY.current = translateY.value;
98
+ scale.value = withTiming(GESTURE_CONSTANTS.scaleAmount, ANIMATION_CONFIG.scale);
99
+ }, [translateX, translateY, scale]);
100
+ const handlePanResponderMove = useCallback((_, gestureState) => {
101
+ const newTranslateX = (startX.current + gestureState.dx) / GESTURE_CONSTANTS.horizontalDamping;
102
+ translateX.value = newTranslateX;
103
+ const newTranslateY = startY.current + gestureState.dy;
104
+ if (newTranslateY < 0) {
105
+ translateY.value = newTranslateY / GESTURE_CONSTANTS.verticalUpDamping;
106
+ }
107
+ else {
108
+ translateY.value = newTranslateY / GESTURE_CONSTANTS.verticalDownDamping;
109
+ }
110
+ }, [translateX, translateY]);
111
+ const handlePanResponderRelease = useCallback((_, gestureState) => {
112
+ translateX.value = withTiming(0, { duration: 100 });
113
+ // 빠른 플리킹 제스처를 했을 때, 혹은 화면의 1/3 이상 내렸을 때, 닫기
114
+ const shouldClose = gestureState.vy > GESTURE_CONSTANTS.closeVelocityThreshold ||
115
+ translateY.value > maxHeight * GESTURE_CONSTANTS.closeDistanceRatio;
116
+ if (shouldClose) {
117
+ translateY.value = withTiming(maxHeight + 100, ANIMATION_CONFIG.close);
118
+ setBottomSheetVisible(false);
119
+ }
120
+ else {
121
+ translateY.value = withTiming(0, ANIMATION_CONFIG.close);
122
+ }
123
+ // 사이즈 원래대로 복귀
124
+ scale.value = withSpring(1, ANIMATION_CONFIG.scaleRestore);
125
+ }, [translateX, translateY, scale, maxHeight, setBottomSheetVisible]);
126
+ const panResponder = useMemo(() => PanResponder.create({
66
127
  onStartShouldSetPanResponder: () => true,
67
128
  onMoveShouldSetPanResponder: () => true,
68
- onPanResponderGrant: () => {
69
- Keyboard.dismiss();
70
- startX.current = translateX.value;
71
- startY.current = translateY.value;
72
- scale.value = withTiming(0.985, { duration: 200 });
129
+ onPanResponderGrant: handlePanResponderGrant,
130
+ onPanResponderMove: handlePanResponderMove,
131
+ onPanResponderRelease: handlePanResponderRelease,
132
+ }), [handlePanResponderGrant, handlePanResponderMove, handlePanResponderRelease]);
133
+ // 배경 터치 핸들러
134
+ const handleBackgroundPress = useCallback(() => {
135
+ if (isBackgroundTouchClose)
136
+ setBottomSheetVisible(false);
137
+ }, [isBackgroundTouchClose, setBottomSheetVisible]);
138
+ const containerStyle = useMemo(() => [
139
+ styles.container,
140
+ {
141
+ width: windowWidth - marginHorizontal * 2,
142
+ height: maxHeight,
143
+ marginHorizontal,
144
+ bottom: marginBottom + bottom,
145
+ backgroundColor: palette.background.base,
73
146
  },
74
- onPanResponderMove: (_, gestureState) => {
75
- const newTranslateX = (startX.current + gestureState.dx) / 18;
76
- translateX.value = newTranslateX;
77
- const newTranslateY = startY.current + gestureState.dy;
78
- if (newTranslateY < 0) {
79
- translateY.value = newTranslateY / 18;
80
- }
81
- else {
82
- translateY.value = newTranslateY / 1.5;
83
- }
84
- },
85
- onPanResponderRelease: (_, gestureState) => {
86
- translateX.value = withTiming(0, { duration: 100 });
87
- // 빠른 플리킹 제스처를 했을 때, 혹은 화면의 1/3 이상 내렸을 때, 닫기
88
- if (gestureState.vy > 0.5 || translateY.value > maxHeight / 3) {
89
- translateY.value = withTiming(maxHeight + 100, { duration: 150 });
90
- setBottomSheetVisible(false);
91
- }
92
- else {
93
- translateY.value = withTiming(0, { duration: 150 });
94
- }
95
- // 사이즈 원래대로 복귀
96
- scale.value = withSpring(1, {
97
- damping: 15,
98
- stiffness: 300
99
- });
100
- },
101
- })).current;
102
- return (!localVisible ? null :
103
- <ModalBackground zIndex={Z_INDEX_VALUE.BOTTOM_SHEET1} key={localVisible ? 'visiblebs' : 'hiddenbs'} modalBgColor={palette.modalBgColor} onPress={() => {
104
- if (isBackgroundTouchClose)
105
- setBottomSheetVisible(false);
106
- }}>
107
- <Animated.View style={[
108
- styles.container,
109
- {
110
- width: windowWidth - marginHorizontal * 2,
111
- height: maxHeight,
112
- marginHorizontal,
113
- bottom: marginBottom + bottom,
114
- backgroundColor: palette.background.base,
115
- },
116
- animatedStyles,
117
- ]}>
118
- <View style={[
119
- styles.pressableView,
120
- { paddingHorizontal: padding, paddingBottom: padding },
121
- ]}>
122
- <View {...panResponder.panHandlers}>
123
- <View style={[styles.gestureBarContainer, { paddingBottom: padding }]}>
124
- <View style={[styles.gestureBar, { backgroundColor: palette.divider }]}/>
125
- </View>
126
- {headerComponent}
147
+ animatedStyles,
148
+ ], [windowWidth, marginHorizontal, maxHeight, marginBottom, bottom, palette.background.base, animatedStyles]);
149
+ const pressableViewStyle = useMemo(() => [
150
+ styles.pressableView,
151
+ { paddingHorizontal: padding, paddingBottom: padding },
152
+ ], [padding]);
153
+ const gestureBarContainerStyle = useMemo(() => [
154
+ styles.gestureBarContainer,
155
+ { paddingBottom: padding }
156
+ ], [padding]);
157
+ const gestureBarStyle = useMemo(() => [
158
+ styles.gestureBar,
159
+ { backgroundColor: palette.divider }
160
+ ], [palette.divider]);
161
+ if (!localVisible) {
162
+ return null;
163
+ }
164
+ return (<ModalBackground zIndex={Z_INDEX_VALUE.BOTTOM_SHEET1} key={localVisible ? 'visiblebs' : 'hiddenbs'} modalBgColor={palette.modalBgColor} onPress={handleBackgroundPress}>
165
+ <Animated.View style={containerStyle}>
166
+ <View style={pressableViewStyle}>
167
+ <View {...panResponder.panHandlers}>
168
+ <View style={gestureBarContainerStyle}>
169
+ <View style={gestureBarStyle}/>
127
170
  </View>
128
-
129
- {component}
171
+ {headerComponent}
130
172
  </View>
131
- </Animated.View>
132
- </ModalBackground>);
173
+
174
+ {component}
175
+ </View>
176
+ </Animated.View>
177
+ </ModalBackground>);
133
178
  }
134
179
  const styles = StyleSheet.create({
135
180
  container: {
@@ -155,5 +200,13 @@ const styles = StyleSheet.create({
155
200
  borderRadius: 2,
156
201
  },
157
202
  });
158
- export default BottomSheetOverlay;
203
+ const arePropsEqual = (prevProps, nextProps) => {
204
+ return (prevProps.headerComponent === nextProps.headerComponent &&
205
+ prevProps.component === nextProps.component &&
206
+ prevProps.options?.isBackgroundTouchClose === nextProps.options?.isBackgroundTouchClose &&
207
+ prevProps.options?.marginHorizontal === nextProps.options?.marginHorizontal &&
208
+ prevProps.options?.marginBottom === nextProps.options?.marginBottom &&
209
+ prevProps.options?.padding === nextProps.options?.padding);
210
+ };
211
+ export default React.memo(BottomSheetOverlay, arePropsEqual);
159
212
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/overlay/BottomSheetOverlay/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useState, useRef } from 'react';\nimport { StyleSheet, View, PanResponder, Keyboard, Platform } from 'react-native';\nimport { useBottomSheet } from '../../model/useOverlay';\nimport Animated, { useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';\nimport ModalBackground from '../ui/ModalBackground';\nimport { useTheme } from '../../model';\nimport { useSafeAreaInsets, initialWindowMetrics } from 'react-native-safe-area-context';\nimport { ShowBottomSheetProps } from '../../model/types';\nimport { MAX_OVERLAY_WIDTH, Z_INDEX_VALUE } from '../../model/utils';\n\nfunction BottomSheetOverlay({\n headerComponent,\n component,\n options = {},\n}: ShowBottomSheetProps) {\n const {\n isBackgroundTouchClose = true,\n marginHorizontal = 10,\n marginBottom = 10,\n padding = 14,\n } = options;\n const { palette, dimensions: { width: windowWidth, height: windowHeight } } = useTheme();\n const { bottomSheetVisible, setBottomSheetVisible, height } = useBottomSheet();\n // 화면의 크기보다 높이가 높으면 화면의 크기로 제한\n const maxHeight = Math.min((windowHeight - 30 - (initialWindowMetrics?.insets.bottom || 0) - (initialWindowMetrics?.insets.top || 0)), height);\n const translateY = useSharedValue(maxHeight);\n const translateX = useSharedValue(0);\n const scale = useSharedValue(1);\n const { bottom } = useSafeAreaInsets();\n const startX = useRef(0);\n const startY = useRef(0);\n const [localVisible, setLocalVisible] = useState(false);\n\n // ** 소프트 키보드 핸들링\n useEffect(() => {\n const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';\n const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';\n\n const keyboardShowSubscription = Keyboard.addListener(showEvent, (event) => {\n translateY.value = withTiming(Platform.OS === 'ios' ? (-event.endCoordinates.height + bottom) : 0, { duration: 300 });\n });\n\n const keyboardHideSubscription = Keyboard.addListener(hideEvent, () => {\n translateY.value = withTiming(0, { duration: 200 });\n });\n\n return () => {\n keyboardShowSubscription.remove();\n keyboardHideSubscription.remove();\n };\n }, []);\n\n useEffect(() => {\n if (bottomSheetVisible) {\n Keyboard.dismiss();\n setLocalVisible(true);\n translateY.value = withSpring(0, {\n damping: 50,\n stiffness: 300,\n mass: 0.7,\n velocity: 100,\n restDisplacementThreshold: 0.2,\n });\n } else {\n translateY.value = withTiming(maxHeight + 100, { duration: 150 });\n setTimeout(() => {\n setLocalVisible(false);\n }, 200);\n }\n }, [bottomSheetVisible]);\n\n const animatedStyles = useAnimatedStyle(() => {\n return {\n transform: [\n { translateY: translateY.value },\n { translateX: translateX.value },\n { scale: scale.value }\n ],\n };\n });\n\n const panResponder = useRef(\n PanResponder.create({\n onStartShouldSetPanResponder: () => true,\n onMoveShouldSetPanResponder: () => true,\n onPanResponderGrant: () => {\n Keyboard.dismiss();\n startX.current = translateX.value;\n startY.current = translateY.value;\n scale.value = withTiming(0.985, { duration: 200 });\n\n },\n onPanResponderMove: (_, gestureState) => {\n const newTranslateX = (startX.current + gestureState.dx) / 18;\n translateX.value = newTranslateX;\n\n const newTranslateY = startY.current + gestureState.dy;\n if (newTranslateY < 0) {\n translateY.value = newTranslateY / 18;\n } else {\n translateY.value = newTranslateY / 1.5;\n }\n },\n onPanResponderRelease: (_, gestureState) => {\n translateX.value = withTiming(0, { duration: 100 });\n\n // 빠른 플리킹 제스처를 했을 때, 혹은 화면의 1/3 이상 내렸을 때, 닫기\n if (gestureState.vy > 0.5 || translateY.value > maxHeight / 3) {\n translateY.value = withTiming(maxHeight + 100, { duration: 150 });\n setBottomSheetVisible(false);\n } else {\n translateY.value = withTiming(0, { duration: 150 });\n }\n\n // 사이즈 원래대로 복귀\n scale.value = withSpring(1, {\n damping: 15,\n stiffness: 300\n });\n },\n })\n ).current;\n\n return (\n !localVisible ? null :\n <ModalBackground\n zIndex={Z_INDEX_VALUE.BOTTOM_SHEET1}\n key={localVisible ? 'visiblebs' : 'hiddenbs'}\n modalBgColor={palette.modalBgColor}\n onPress={() => {\n if (isBackgroundTouchClose) setBottomSheetVisible(false);\n }}\n >\n <Animated.View\n style={[\n styles.container,\n {\n width: windowWidth - marginHorizontal * 2,\n height: maxHeight,\n marginHorizontal,\n bottom: marginBottom + bottom,\n backgroundColor: palette.background.base,\n },\n animatedStyles,\n ]}\n >\n <View\n style={[\n styles.pressableView,\n { paddingHorizontal: padding, paddingBottom: padding },\n ]}\n >\n <View {...panResponder.panHandlers}>\n <View style={[styles.gestureBarContainer, { paddingBottom: padding }]}>\n <View style={[styles.gestureBar, { backgroundColor: palette.divider }]} />\n </View>\n {headerComponent}\n </View>\n\n {component}\n </View>\n </Animated.View>\n </ModalBackground>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n position: 'absolute',\n borderRadius: 26,\n overflow: 'hidden',\n zIndex: Z_INDEX_VALUE.BOTTOM_SHEET2,\n maxWidth: MAX_OVERLAY_WIDTH,\n },\n pressableView: {\n width: '100%',\n height: '100%',\n },\n gestureBarContainer: {\n width: '100%',\n paddingTop: 10,\n justifyContent: 'center',\n alignItems: 'center',\n },\n gestureBar: {\n width: 45,\n height: 3,\n borderRadius: 2,\n },\n});\n\nexport default BottomSheetOverlay;"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/overlay/BottomSheetOverlay/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useState, useRef, useCallback, useMemo } from 'react';\nimport { StyleSheet, View, PanResponder, Keyboard, Platform } from 'react-native';\nimport { useBottomSheet } from '../../model/useOverlay';\nimport Animated, { useAnimatedStyle, useSharedValue, withSpring, withTiming } from 'react-native-reanimated';\nimport ModalBackground from '../ui/ModalBackground';\nimport { useTheme } from '../../model';\nimport { useSafeAreaInsets, initialWindowMetrics } from 'react-native-safe-area-context';\nimport { ShowBottomSheetProps } from '../../model/types';\nimport { MAX_OVERLAY_WIDTH, Z_INDEX_VALUE } from '../../model/utils';\n\nconst IS_IOS = Platform.OS === 'ios';\n\nconst keyboardEvents = ({\n showEvent: IS_IOS ? 'keyboardWillShow' as const : 'keyboardDidShow' as const,\n hideEvent: IS_IOS ? 'keyboardWillHide' as const : 'keyboardDidHide' as const,\n});\n\nconst ANIMATION_CONFIG = {\n keyboard: {\n show: { duration: IS_IOS ? 250 : 300 },\n hide: { duration: IS_IOS ? 150 : 200 },\n },\n spring: {\n damping: 50,\n stiffness: 300,\n mass: 0.7,\n velocity: 100,\n restDisplacementThreshold: 0.2,\n },\n close: { duration: 150 },\n scale: { duration: 200 },\n scaleRestore: {\n damping: 15,\n stiffness: 300,\n },\n} as const;\n\nconst GESTURE_CONSTANTS = {\n scaleAmount: 0.985,\n horizontalDamping: 18,\n verticalUpDamping: 18,\n verticalDownDamping: 1.5,\n closeVelocityThreshold: 0.5,\n closeDistanceRatio: 1 / 3,\n hideDelay: 200,\n} as const;\n\nfunction BottomSheetOverlay({\n headerComponent,\n component,\n options = {},\n}: ShowBottomSheetProps) {\n const {\n isBackgroundTouchClose = true,\n marginHorizontal = 10,\n marginBottom = 10,\n padding = 14,\n } = options;\n const { palette, dimensions: { width: windowWidth, height: windowHeight } } = useTheme();\n const { bottomSheetVisible, setBottomSheetVisible, height } = useBottomSheet();\n const { bottom } = useSafeAreaInsets();\n \n // 화면의 크기보다 높이가 높으면 화면의 크기로 제한 \n const maxHeight = useMemo(() => \n Math.min(\n windowHeight - 30 - (initialWindowMetrics?.insets.bottom || 0) - (initialWindowMetrics?.insets.top || 0),\n height\n ),\n [windowHeight, height]\n );\n \n const translateY = useRef(useSharedValue(0)).current;\n const translateX = useRef(useSharedValue(0)).current;\n const scale = useRef(useSharedValue(1)).current;\n \n const startX = useRef(0);\n const startY = useRef(0);\n const [localVisible, setLocalVisible] = useState(false);\n\n const handleKeyboardShow = useCallback((event: any) => {\n const targetY = IS_IOS ? (-event.endCoordinates.height + bottom) : 0;\n translateY.value = withTiming(targetY, ANIMATION_CONFIG.keyboard.show);\n }, [translateY, bottom]);\n\n const handleKeyboardHide = useCallback(() => {\n translateY.value = withTiming(0, ANIMATION_CONFIG.keyboard.hide);\n }, [translateY]);\n\n // 소프트 키보드 핸들링\n useEffect(() => {\n const keyboardShowSubscription = Keyboard.addListener(keyboardEvents.showEvent, handleKeyboardShow);\n const keyboardHideSubscription = Keyboard.addListener(keyboardEvents.hideEvent, handleKeyboardHide);\n\n return () => {\n keyboardShowSubscription.remove();\n keyboardHideSubscription.remove();\n };\n }, [keyboardEvents.showEvent, keyboardEvents.hideEvent, handleKeyboardShow, handleKeyboardHide]);\n\n // BottomSheet 표시/숨김 애니메이션 처리\n useEffect(() => {\n if (bottomSheetVisible) {\n Keyboard.dismiss();\n setLocalVisible(true);\n translateY.value = withSpring(0, ANIMATION_CONFIG.spring);\n } else {\n translateY.value = withTiming(maxHeight + 100, ANIMATION_CONFIG.close);\n setTimeout(() => {\n setLocalVisible(false);\n }, GESTURE_CONSTANTS.hideDelay);\n }\n }, [bottomSheetVisible, translateY, maxHeight]);\n\n const animatedStyles = useAnimatedStyle(() => {\n return {\n transform: [\n { translateY: translateY.value },\n { translateX: translateX.value },\n { scale: scale.value }\n ],\n };\n }, []);\n\n const handlePanResponderGrant = useCallback(() => {\n Keyboard.dismiss();\n startX.current = translateX.value;\n startY.current = translateY.value;\n scale.value = withTiming(GESTURE_CONSTANTS.scaleAmount, ANIMATION_CONFIG.scale);\n }, [translateX, translateY, scale]);\n\n const handlePanResponderMove = useCallback((_, gestureState) => {\n const newTranslateX = (startX.current + gestureState.dx) / GESTURE_CONSTANTS.horizontalDamping;\n translateX.value = newTranslateX;\n\n const newTranslateY = startY.current + gestureState.dy;\n if (newTranslateY < 0) {\n translateY.value = newTranslateY / GESTURE_CONSTANTS.verticalUpDamping;\n } else {\n translateY.value = newTranslateY / GESTURE_CONSTANTS.verticalDownDamping;\n }\n }, [translateX, translateY]);\n\n const handlePanResponderRelease = useCallback((_, gestureState) => {\n translateX.value = withTiming(0, { duration: 100 });\n\n // 빠른 플리킹 제스처를 했을 때, 혹은 화면의 1/3 이상 내렸을 때, 닫기\n const shouldClose = gestureState.vy > GESTURE_CONSTANTS.closeVelocityThreshold || \n translateY.value > maxHeight * GESTURE_CONSTANTS.closeDistanceRatio;\n \n if (shouldClose) {\n translateY.value = withTiming(maxHeight + 100, ANIMATION_CONFIG.close);\n setBottomSheetVisible(false);\n } else {\n translateY.value = withTiming(0, ANIMATION_CONFIG.close);\n }\n\n // 사이즈 원래대로 복귀\n scale.value = withSpring(1, ANIMATION_CONFIG.scaleRestore);\n }, [translateX, translateY, scale, maxHeight, setBottomSheetVisible]);\n\n const panResponder = useMemo(\n () => PanResponder.create({\n onStartShouldSetPanResponder: () => true,\n onMoveShouldSetPanResponder: () => true,\n onPanResponderGrant: handlePanResponderGrant,\n onPanResponderMove: handlePanResponderMove,\n onPanResponderRelease: handlePanResponderRelease,\n }),\n [handlePanResponderGrant, handlePanResponderMove, handlePanResponderRelease]\n );\n\n // 배경 터치 핸들러\n const handleBackgroundPress = useCallback(() => {\n if (isBackgroundTouchClose) setBottomSheetVisible(false);\n }, [isBackgroundTouchClose, setBottomSheetVisible]);\n\n const containerStyle = useMemo(() => [\n styles.container,\n {\n width: windowWidth - marginHorizontal * 2,\n height: maxHeight,\n marginHorizontal,\n bottom: marginBottom + bottom,\n backgroundColor: palette.background.base,\n },\n animatedStyles,\n ], [windowWidth, marginHorizontal, maxHeight, marginBottom, bottom, palette.background.base, animatedStyles]);\n\n const pressableViewStyle = useMemo(() => [\n styles.pressableView,\n { paddingHorizontal: padding, paddingBottom: padding },\n ], [padding]);\n\n const gestureBarContainerStyle = useMemo(() => [\n styles.gestureBarContainer,\n { paddingBottom: padding }\n ], [padding]);\n\n const gestureBarStyle = useMemo(() => [\n styles.gestureBar,\n { backgroundColor: palette.divider }\n ], [palette.divider]);\n\n if (!localVisible) {\n return null;\n }\n\n return (\n <ModalBackground\n zIndex={Z_INDEX_VALUE.BOTTOM_SHEET1}\n key={localVisible ? 'visiblebs' : 'hiddenbs'}\n modalBgColor={palette.modalBgColor}\n onPress={handleBackgroundPress}\n >\n <Animated.View style={containerStyle}>\n <View style={pressableViewStyle}>\n <View {...panResponder.panHandlers}>\n <View style={gestureBarContainerStyle}>\n <View style={gestureBarStyle} />\n </View>\n {headerComponent}\n </View>\n\n {component}\n </View>\n </Animated.View>\n </ModalBackground>\n );\n}\n\nconst styles = StyleSheet.create({\n container: {\n position: 'absolute',\n borderRadius: 26,\n overflow: 'hidden',\n zIndex: Z_INDEX_VALUE.BOTTOM_SHEET2,\n maxWidth: MAX_OVERLAY_WIDTH,\n },\n pressableView: {\n width: '100%',\n height: '100%',\n },\n gestureBarContainer: {\n width: '100%',\n paddingTop: 10,\n justifyContent: 'center',\n alignItems: 'center',\n },\n gestureBar: {\n width: 45,\n height: 3,\n borderRadius: 2,\n },\n});\n\nconst arePropsEqual = (\n prevProps: ShowBottomSheetProps, \n nextProps: ShowBottomSheetProps\n): boolean => {\n return (\n prevProps.headerComponent === nextProps.headerComponent &&\n prevProps.component === nextProps.component &&\n prevProps.options?.isBackgroundTouchClose === nextProps.options?.isBackgroundTouchClose &&\n prevProps.options?.marginHorizontal === nextProps.options?.marginHorizontal &&\n prevProps.options?.marginBottom === nextProps.options?.marginBottom &&\n prevProps.options?.padding === nextProps.options?.padding\n );\n};\n\nexport default React.memo(BottomSheetOverlay, arePropsEqual);"]}
@@ -0,0 +1,11 @@
1
+ import React, { ReactNode } from 'react';
2
+ interface PortalProviderProps {
3
+ children: ReactNode;
4
+ }
5
+ export declare const PortalProvider: React.FC<PortalProviderProps>;
6
+ interface PortalProps {
7
+ children: ReactNode;
8
+ }
9
+ export declare const ZSPortal: React.FC<PortalProps>;
10
+ export {};
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/overlay/ZSPortal/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAoD,SAAS,EAAa,MAAM,OAAO,CAAC;AAUtG,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,EAAE,KAAK,CAAC,EAAE,CAAC,mBAAmB,CAqCxD,CAAC;AAIF,UAAU,WAAW;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,WAAW,CAe1C,CAAC"}
@@ -0,0 +1,49 @@
1
+ import React, { createContext, useContext, useState, useCallback, useEffect } from 'react';
2
+ import Animated, { FadeInDown, FadeOutDown } from 'react-native-reanimated';
3
+ const PortalContext = createContext(null);
4
+ export const PortalProvider = ({ children }) => {
5
+ const [portals, setPortals] = useState(new Map());
6
+ const registerPortal = useCallback((id, element) => {
7
+ setPortals(prev => {
8
+ // 동일한 요소가 이미 등록되어 있다면 업데이트하지 않음
9
+ if (prev.get(id) === element) {
10
+ return prev;
11
+ }
12
+ const newMap = new Map(prev);
13
+ newMap.set(id, element);
14
+ return newMap;
15
+ });
16
+ }, []);
17
+ const unregisterPortal = useCallback((id) => {
18
+ setPortals(prev => {
19
+ // 해당 ID가 존재하지 않으면 상태를 변경하지 않음
20
+ if (!prev.has(id)) {
21
+ return prev;
22
+ }
23
+ const newMap = new Map(prev);
24
+ newMap.delete(id);
25
+ return newMap;
26
+ });
27
+ }, []);
28
+ return (<PortalContext.Provider value={{ registerPortal, unregisterPortal }}>
29
+ {children}
30
+ {Array.from(portals.entries()).map(([id, element]) => (<Animated.View entering={FadeInDown} exiting={FadeOutDown} key={id} style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, pointerEvents: 'box-none' }}>
31
+ {element}
32
+ </Animated.View>))}
33
+ </PortalContext.Provider>);
34
+ };
35
+ export const ZSPortal = ({ children }) => {
36
+ const context = useContext(PortalContext);
37
+ const [portalId] = useState(() => `portal_${Date.now()}_${Math.random()}`);
38
+ useEffect(() => {
39
+ if (context) {
40
+ context.registerPortal(portalId, children);
41
+ return () => {
42
+ context.unregisterPortal(portalId);
43
+ };
44
+ }
45
+ return () => { };
46
+ }, [children]);
47
+ return null;
48
+ };
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/overlay/ZSPortal/index.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { createContext, useContext, useState, useCallback, ReactNode, useEffect } from 'react';\nimport Animated, { FadeInDown, FadeOutDown } from 'react-native-reanimated';\n\ninterface PortalContextType {\n registerPortal: (id: string, element: ReactNode) => void;\n unregisterPortal: (id: string) => void;\n}\n\nconst PortalContext = createContext<PortalContextType | null>(null);\n\ninterface PortalProviderProps {\n children: ReactNode;\n}\n\nexport const PortalProvider: React.FC<PortalProviderProps> = ({ children }) => {\n const [portals, setPortals] = useState<Map<string, ReactNode>>(new Map());\n\n const registerPortal = useCallback((id: string, element: ReactNode) => {\n setPortals(prev => {\n // 동일한 요소가 이미 등록되어 있다면 업데이트하지 않음\n if (prev.get(id) === element) {\n return prev;\n }\n const newMap = new Map(prev);\n newMap.set(id, element);\n return newMap;\n });\n }, []);\n\n const unregisterPortal = useCallback((id: string) => {\n setPortals(prev => {\n // 해당 ID가 존재하지 않으면 상태를 변경하지 않음\n if (!prev.has(id)) {\n return prev;\n }\n const newMap = new Map(prev);\n newMap.delete(id);\n return newMap;\n });\n }, []);\n\n return (\n <PortalContext.Provider value={{ registerPortal, unregisterPortal }}>\n {children}\n {Array.from(portals.entries()).map(([id, element]) => (\n <Animated.View entering={FadeInDown} exiting={FadeOutDown} key={id} style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, pointerEvents: 'box-none' }}>\n {element}\n </Animated.View>\n ))}\n </PortalContext.Provider>\n );\n};\n\n// ------------------------------------------------------------------------------------------------\n\ninterface PortalProps {\n children: ReactNode;\n}\n\nexport const ZSPortal: React.FC<PortalProps> = ({ children }) => {\n const context = useContext(PortalContext);\n const [portalId] = useState(() => `portal_${Date.now()}_${Math.random()}`);\n\n useEffect(() => {\n if (context) {\n context.registerPortal(portalId, children);\n return () => {\n context.unregisterPortal(portalId);\n };\n }\n return () => {};\n }, [children]);\n\n return null;\n};\n"]}
@@ -5,6 +5,7 @@ import * as useOverlayProvider from '../model/useOverlayProvider';
5
5
  import { useOverlay } from '../model/useOverlay';
6
6
  import PopOverButton from './PopOver/PopOverButton';
7
7
  import PopOverMenu from './PopOver/PopOverMenu';
8
+ import { ZSPortal } from './ZSPortal';
8
9
  import * as types from '../model/types';
9
- export { AlertOverlay, BottomSheetOverlay, SnackbarNotify, useOverlayProvider, useOverlay, PopOverButton, PopOverMenu, types, };
10
+ export { AlertOverlay, BottomSheetOverlay, SnackbarNotify, useOverlayProvider, useOverlay, PopOverButton, PopOverMenu, ZSPortal, types, };
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/overlay/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAC1C,OAAO,kBAAkB,MAAM,sBAAsB,CAAC;AACtD,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,kBAAkB,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,aAAa,MAAM,yBAAyB,CAAC;AACpD,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AAExC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,WAAW,EACX,KAAK,GACN,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/overlay/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAC1C,OAAO,kBAAkB,MAAM,sBAAsB,CAAC;AACtD,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,kBAAkB,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,aAAa,MAAM,yBAAyB,CAAC;AACpD,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AAExC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,WAAW,EACX,QAAQ,EACR,KAAK,GACN,CAAA"}
@@ -5,6 +5,7 @@ import * as useOverlayProvider from '../model/useOverlayProvider';
5
5
  import { useOverlay } from '../model/useOverlay';
6
6
  import PopOverButton from './PopOver/PopOverButton';
7
7
  import PopOverMenu from './PopOver/PopOverMenu';
8
+ import { ZSPortal } from './ZSPortal';
8
9
  import * as types from '../model/types';
9
- export { AlertOverlay, BottomSheetOverlay, SnackbarNotify, useOverlayProvider, useOverlay, PopOverButton, PopOverMenu, types, };
10
+ export { AlertOverlay, BottomSheetOverlay, SnackbarNotify, useOverlayProvider, useOverlay, PopOverButton, PopOverMenu, ZSPortal, types, };
10
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/overlay/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAC1C,OAAO,kBAAkB,MAAM,sBAAsB,CAAC;AACtD,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,kBAAkB,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,aAAa,MAAM,yBAAyB,CAAC;AACpD,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AAExC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,WAAW,EACX,KAAK,GACN,CAAA","sourcesContent":["import AlertOverlay from './AlertOverlay';\nimport BottomSheetOverlay from './BottomSheetOverlay';\nimport SnackbarNotify from './SnackbarNotify';\nimport * as useOverlayProvider from '../model/useOverlayProvider';\nimport { useOverlay } from '../model/useOverlay';\nimport PopOverButton from './PopOver/PopOverButton';\nimport PopOverMenu from './PopOver/PopOverMenu';\nimport * as types from '../model/types';\n\nexport {\n AlertOverlay,\n BottomSheetOverlay,\n SnackbarNotify,\n useOverlayProvider,\n useOverlay,\n PopOverButton,\n PopOverMenu,\n types,\n}"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/overlay/index.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,gBAAgB,CAAC;AAC1C,OAAO,kBAAkB,MAAM,sBAAsB,CAAC;AACtD,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,kBAAkB,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,aAAa,MAAM,yBAAyB,CAAC;AACpD,OAAO,WAAW,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AAExC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,aAAa,EACb,WAAW,EACX,QAAQ,EACR,KAAK,GACN,CAAA","sourcesContent":["import AlertOverlay from './AlertOverlay';\nimport BottomSheetOverlay from './BottomSheetOverlay';\nimport SnackbarNotify from './SnackbarNotify';\nimport * as useOverlayProvider from '../model/useOverlayProvider';\nimport { useOverlay } from '../model/useOverlay';\nimport PopOverButton from './PopOver/PopOverButton';\nimport PopOverMenu from './PopOver/PopOverMenu';\nimport { ZSPortal } from './ZSPortal';\nimport * as types from '../model/types';\n\nexport {\n AlertOverlay,\n BottomSheetOverlay,\n SnackbarNotify,\n useOverlayProvider,\n useOverlay,\n PopOverButton,\n PopOverMenu,\n ZSPortal,\n types,\n}"]}
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ children: React.ReactNode;
3
+ keyboardShowOffset?: number;
4
+ keyboardHideOffset?: number;
5
+ handleLayoutHeight?: (height: number) => void;
6
+ }
7
+ declare function ZSAboveKeyboard({ keyboardShowOffset, keyboardHideOffset, children, handleLayoutHeight, }: Props): import("react").JSX.Element;
8
+ export default ZSAboveKeyboard;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSAboveKeyboard/index.tsx"],"names":[],"mappings":"AAUA,UAAU,KAAK;IACb,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAED,iBAAS,eAAe,CAAC,EACvB,kBAAsB,EACtB,kBAAsB,EACtB,QAAQ,EACR,kBAAkB,GACnB,EAAE,KAAK,+BA2CP;AAED,eAAe,eAAe,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { Keyboard, Platform, StyleSheet, Dimensions, View } from 'react-native';
3
+ import { Z_INDEX_VALUE } from '../../model/utils';
4
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
+ import { ZSPortal } from '../../overlay';
6
+ const windowHeight = Dimensions.get('window').height;
7
+ const showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';
8
+ const hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';
9
+ function ZSAboveKeyboard({ keyboardShowOffset = 0, keyboardHideOffset = 0, children, handleLayoutHeight, }) {
10
+ const [topValue, setTopValue] = useState(0);
11
+ const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);
12
+ const [componentHeight, setComponentHeight] = useState(0);
13
+ const { bottom } = useSafeAreaInsets();
14
+ // const statusBarHeight = StatusBar.currentHeight || 0;
15
+ useEffect(() => {
16
+ const keyboardShowSubscription = Keyboard.addListener(showEvent, (event) => {
17
+ // 키보드 바로 위에 위치하도록 계산
18
+ const topValue = windowHeight - event.endCoordinates.height - componentHeight - keyboardShowOffset - 0;
19
+ // 구형폰에서는 상태바 높이를 빼야함
20
+ // const topValue = windowHeight - event.endCoordinates.height - componentHeight - keyboardShowOffset - statusBarHeight;
21
+ setTopValue(topValue);
22
+ setIsKeyboardVisible(true);
23
+ });
24
+ const keyboardHideSubscription = Keyboard.addListener(hideEvent, () => {
25
+ setTopValue(0);
26
+ setIsKeyboardVisible(false);
27
+ });
28
+ return () => {
29
+ keyboardShowSubscription.remove();
30
+ keyboardHideSubscription.remove();
31
+ };
32
+ }, [componentHeight]);
33
+ const handleLayout = (event) => {
34
+ const { height } = event.nativeEvent.layout;
35
+ setComponentHeight(height);
36
+ handleLayoutHeight?.(height);
37
+ };
38
+ return (<ZSPortal>
39
+ <View style={[styles.container, isKeyboardVisible ? { top: topValue } : { bottom: keyboardHideOffset + bottom }]}>
40
+ <View onLayout={handleLayout}>
41
+ {children}
42
+ </View>
43
+ </View>
44
+ </ZSPortal>);
45
+ }
46
+ export default ZSAboveKeyboard;
47
+ const styles = StyleSheet.create({
48
+ container: {
49
+ position: 'absolute',
50
+ justifyContent: 'center',
51
+ alignItems: 'center',
52
+ zIndex: Z_INDEX_VALUE.ABOVE_KEYBOARD,
53
+ width: '100%',
54
+ },
55
+ });
56
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/ui/ZSAboveKeyboard/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAgC,MAAM,cAAc,CAAC;AAC9G,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC;AACrD,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,iBAAiB,CAAC;AACjF,MAAM,SAAS,GAAG,QAAQ,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,iBAAiB,CAAC;AASjF,SAAS,eAAe,CAAC,EACvB,kBAAkB,GAAG,CAAC,EACtB,kBAAkB,GAAG,CAAC,EACtB,QAAQ,EACR,kBAAkB,GACZ;IACN,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC5C,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClE,MAAM,CAAC,eAAe,EAAE,kBAAkB,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAAC;IACvC,wDAAwD;IAExD,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,wBAAwB,GAAG,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;YACzE,qBAAqB;YACrB,MAAM,QAAQ,GAAG,YAAY,GAAG,KAAK,CAAC,cAAc,CAAC,MAAM,GAAG,eAAe,GAAG,kBAAkB,GAAG,CAAC,CAAC;YACvG,qBAAqB;YACrB,wHAAwH;YACxH,WAAW,CAAC,QAAQ,CAAC,CAAC;YACtB,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,MAAM,wBAAwB,GAAG,QAAQ,CAAC,WAAW,CAAC,SAAS,EAAE,GAAG,EAAE;YACpE,WAAW,CAAC,CAAC,CAAC,CAAC;YACf,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE;YACV,wBAAwB,CAAC,MAAM,EAAE,CAAC;YAClC,wBAAwB,CAAC,MAAM,EAAE,CAAC;QACpC,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IAEtB,MAAM,YAAY,GAAG,CAAC,KAAwB,EAAE,EAAE;QAChD,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC;QAC5C,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAC3B,kBAAkB,EAAE,CAAC,MAAM,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,OAAO,CACL,CAAC,QAAQ,CACP;MAAA,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAAC,CAAC,CAC/G;QAAA,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,CAC3B;UAAA,CAAC,QAAQ,CACX;QAAA,EAAE,IAAI,CACR;MAAA,EAAE,IAAI,CACR;IAAA,EAAE,QAAQ,CAAC,CACZ,CAAC;AACJ,CAAC;AAED,eAAe,eAAe,CAAC;AAE/B,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC;IAC/B,SAAS,EAAE;QACT,QAAQ,EAAE,UAAU;QACpB,cAAc,EAAE,QAAQ;QACxB,UAAU,EAAE,QAAQ;QACpB,MAAM,EAAE,aAAa,CAAC,cAAc;QACpC,KAAK,EAAE,MAAM;KACd;CACF,CAAC,CAAC","sourcesContent":["import { useEffect, useState } from 'react';\nimport { Keyboard, Platform, StyleSheet, Dimensions, View, StatusBar, LayoutChangeEvent } from 'react-native';\nimport { Z_INDEX_VALUE } from '../../model/utils';\nimport { useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { ZSPortal } from '../../overlay';\n\nconst windowHeight = Dimensions.get('window').height;\nconst showEvent = Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow';\nconst hideEvent = Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide';\n\ninterface Props {\n children: React.ReactNode;\n keyboardShowOffset?: number;\n keyboardHideOffset?: number;\n handleLayoutHeight?: (height: number) => void;\n}\n\nfunction ZSAboveKeyboard({\n keyboardShowOffset = 0,\n keyboardHideOffset = 0,\n children,\n handleLayoutHeight,\n}: Props) {\n const [topValue, setTopValue] = useState(0);\n const [isKeyboardVisible, setIsKeyboardVisible] = useState(false);\n const [componentHeight, setComponentHeight] = useState(0);\n const { bottom } = useSafeAreaInsets();\n // const statusBarHeight = StatusBar.currentHeight || 0;\n\n useEffect(() => {\n const keyboardShowSubscription = Keyboard.addListener(showEvent, (event) => {\n // 키보드 바로 위에 위치하도록 계산\n const topValue = windowHeight - event.endCoordinates.height - componentHeight - keyboardShowOffset - 0;\n // 구형폰에서는 상태바 높이를 빼야함\n // const topValue = windowHeight - event.endCoordinates.height - componentHeight - keyboardShowOffset - statusBarHeight;\n setTopValue(topValue);\n setIsKeyboardVisible(true);\n });\n\n const keyboardHideSubscription = Keyboard.addListener(hideEvent, () => {\n setTopValue(0);\n setIsKeyboardVisible(false);\n });\n\n return () => {\n keyboardShowSubscription.remove();\n keyboardHideSubscription.remove();\n };\n }, [componentHeight]);\n\n const handleLayout = (event: LayoutChangeEvent) => {\n const { height } = event.nativeEvent.layout;\n setComponentHeight(height);\n handleLayoutHeight?.(height);\n };\n\n return (\n <ZSPortal>\n <View style={[styles.container, isKeyboardVisible ? { top: topValue } : { bottom: keyboardHideOffset + bottom }]}>\n <View onLayout={handleLayout}>\n {children}\n </View>\n </View>\n </ZSPortal>\n );\n}\n\nexport default ZSAboveKeyboard;\n\nconst styles = StyleSheet.create({\n container: {\n position: 'absolute',\n justifyContent: 'center',\n alignItems: 'center',\n zIndex: Z_INDEX_VALUE.ABOVE_KEYBOARD,\n width: '100%',\n },\n});\n"]}
@@ -14,9 +14,21 @@ export type ZSContainerProps = ViewProps & {
14
14
  translucent?: boolean;
15
15
  onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
16
16
  scrollEventThrottle?: number;
17
+ scrollToFocusedInput?: boolean;
17
18
  };
18
19
  export type ZSContainerRef = ScrollView;
19
- declare const ZSContainer: React.ForwardRefExoticComponent<ViewProps & {
20
+ export declare const styles: {
21
+ flex1: {
22
+ flex: number;
23
+ width: "100%";
24
+ };
25
+ scrollContainerStyle: {
26
+ flexGrow: number;
27
+ alignItems: "center";
28
+ width: "100%";
29
+ };
30
+ };
31
+ declare const _default: React.MemoExoticComponent<React.ForwardRefExoticComponent<ViewProps & {
20
32
  backgroundColor?: string;
21
33
  statusBarColor?: string;
22
34
  barStyle?: "light-content" | "dark-content";
@@ -30,17 +42,7 @@ declare const ZSContainer: React.ForwardRefExoticComponent<ViewProps & {
30
42
  translucent?: boolean;
31
43
  onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
32
44
  scrollEventThrottle?: number;
33
- } & React.RefAttributes<ScrollView>>;
34
- export declare const styles: {
35
- flex1: {
36
- flex: number;
37
- width: "100%";
38
- };
39
- scrollContainerStyle: {
40
- flexGrow: number;
41
- alignItems: "center";
42
- width: "100%";
43
- };
44
- };
45
- export default ZSContainer;
45
+ scrollToFocusedInput?: boolean;
46
+ } & React.RefAttributes<ScrollView>>>;
47
+ export default _default;
46
48
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSContainer/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAgE,MAAM,OAAO,CAAC;AACvG,OAAO,EAAE,SAAS,EAAyB,UAAU,EAAE,oBAAoB,EAAE,iBAAiB,EAA4B,MAAM,cAAc,CAAC;AAI/I,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG;IACzC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,eAAe,GAAG,cAAc,CAAC;IAC5C,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC,CAAC;IACnD,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;IACpE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC;AAExC,QAAA,MAAM,WAAW;sBAjBG,MAAM;qBACP,MAAM;eACZ,eAAe,GAAG,cAAc;YACnC,KAAK,CAAC,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;yBAC7B,OAAO;mBACb,SAAS;sBACN,SAAS;qBACV,SAAS;mCACK,OAAO;gCACV,MAAM;kBACpB,OAAO;eACV,CAAC,KAAK,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,KAAK,IAAI;0BAC7C,MAAM;oCA+H5B,CAAC;AAEH,eAAO,MAAM,MAAM;;;;;;;;;;CAGjB,CAAC;AAEH,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/ui/ZSContainer/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,SAAS,EAAsF,MAAM,OAAO,CAAC;AAC7H,OAAO,EAAE,SAAS,EAAyB,UAAU,EAAE,oBAAoB,EAAE,iBAAiB,EAA4B,MAAM,cAAc,CAAC;AAW/I,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG;IACzC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,eAAe,GAAG,cAAc,CAAC;IAC5C,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC,CAAC;IACnD,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAC;IACpE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC;AAiJxC,eAAO,MAAM,MAAM;;;;;;;;;;CAGjB,CAAC;;sBApKiB,MAAM;qBACP,MAAM;eACZ,eAAe,GAAG,cAAc;YACnC,KAAK,CAAC,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;yBAC7B,OAAO;mBACb,SAAS;sBACN,SAAS;qBACV,SAAS;mCACK,OAAO;gCACV,MAAM;kBACpB,OAAO;eACV,CAAC,KAAK,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,KAAK,IAAI;0BAC7C,MAAM;2BACL,OAAO;;AAkLhC,wBAAsD"}