@chem-po/react-native 0.0.52 → 0.0.53
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/package.json +5 -20
- package/src/components/box/Center.tsx +0 -19
- package/src/components/box/CollapseHorizontal.tsx +0 -44
- package/src/components/box/ContentBox.tsx +0 -24
- package/src/components/box/DropShadow.tsx +0 -28
- package/src/components/box/ExpandOnMount.tsx +0 -74
- package/src/components/box/Expandable.tsx +0 -143
- package/src/components/box/FullSizeContainer.tsx +0 -64
- package/src/components/box/index.ts +0 -7
- package/src/components/button/ActionButton.tsx +0 -196
- package/src/components/button/ButtonText.tsx +0 -60
- package/src/components/button/DeleteButton.tsx +0 -288
- package/src/components/button/LoadingButton.tsx +0 -41
- package/src/components/button/Toggle.tsx +0 -109
- package/src/components/button/hooks.ts +0 -66
- package/src/components/button/index.ts +0 -5
- package/src/components/feed/FeedContentPane.tsx +0 -97
- package/src/components/feed/MediaFeed.tsx +0 -199
- package/src/components/feed/MediaFeedBackground.tsx +0 -136
- package/src/components/feed/MediaFeedRefresh.tsx +0 -113
- package/src/components/feed/constants.ts +0 -2
- package/src/components/feed/context.tsx +0 -19
- package/src/components/feed/hooks.ts +0 -279
- package/src/components/feed/index.ts +0 -2
- package/src/components/form/Condition.tsx +0 -27
- package/src/components/form/Field.tsx +0 -44
- package/src/components/form/Form.tsx +0 -452
- package/src/components/form/FormFooter.tsx +0 -164
- package/src/components/form/UploadProgress/index.tsx +0 -50
- package/src/components/form/index.ts +0 -3
- package/src/components/form/input/Editable.tsx +0 -206
- package/src/components/form/input/InputSlider.tsx +0 -71
- package/src/components/form/input/OptionalTag.tsx +0 -43
- package/src/components/form/input/StandaloneInput.tsx +0 -49
- package/src/components/form/input/boolean/index.tsx +0 -53
- package/src/components/form/input/color/index.tsx +0 -145
- package/src/components/form/input/common/InputClearButton.tsx +0 -57
- package/src/components/form/input/date/index.tsx +0 -125
- package/src/components/form/input/datetime/index.tsx +0 -176
- package/src/components/form/input/file/index.tsx +0 -310
- package/src/components/form/input/hooks/index.ts +0 -2
- package/src/components/form/input/hooks/useInputColor.ts +0 -7
- package/src/components/form/input/hooks/useInputImperativeHandle.ts +0 -22
- package/src/components/form/input/hooks/useInputStyles.ts +0 -114
- package/src/components/form/input/index.ts +0 -4
- package/src/components/form/input/input.tsx +0 -218
- package/src/components/form/input/multipleSelect/index.tsx +0 -221
- package/src/components/form/input/number/index.tsx +0 -108
- package/src/components/form/input/select/index.tsx +0 -152
- package/src/components/form/input/socialMedia/index.tsx +0 -235
- package/src/components/form/input/text/AutoResizeTextarea.tsx +0 -41
- package/src/components/form/input/text/index.tsx +0 -99
- package/src/components/form/input/text/textarea.tsx +0 -32
- package/src/components/form/input/text/useWebAutoResize.tsx +0 -73
- package/src/components/form/input/time/index.tsx +0 -125
- package/src/components/form/types.ts +0 -8
- package/src/components/form/view/file.tsx +0 -80
- package/src/components/form/view/index.tsx +0 -125
- package/src/components/form/view/multipleSelect.tsx +0 -85
- package/src/components/form/view/select.tsx +0 -83
- package/src/components/form/view/styles.ts +0 -12
- package/src/components/icons/index.tsx +0 -28
- package/src/components/image/ImageViewModal.tsx +0 -319
- package/src/components/image/index.ts +0 -1
- package/src/components/index.ts +0 -8
- package/src/components/layout/CollapseHorizontal.tsx +0 -92
- package/src/components/loading/CircularProgress.tsx +0 -56
- package/src/components/loading/Loading.tsx +0 -146
- package/src/components/loading/LoadingImage.tsx +0 -163
- package/src/components/loading/LoadingOverlay.tsx +0 -74
- package/src/components/loading/LoadingSwitch.tsx +0 -110
- package/src/components/loading/ProgressBar.tsx +0 -75
- package/src/components/loading/index.ts +0 -6
- package/src/components/text/AnimatedText.tsx +0 -68
- package/src/components/text/Txt.tsx +0 -12
- package/src/components/text/index.ts +0 -1
- package/src/components/theme/colorMode/DarkModeToggle.tsx +0 -47
- package/src/components/theme/colorMode/index.ts +0 -1
- package/src/components/theme/index.ts +0 -1
- package/src/constants/index.ts +0 -1
- package/src/constants/toast.ts +0 -24
- package/src/contexts/fonts.tsx +0 -23
- package/src/contexts/index.ts +0 -1
- package/src/contexts/root.tsx +0 -190
- package/src/hooks/index.ts +0 -3
- package/src/hooks/useFadeIn.ts +0 -48
- package/src/hooks/useFont.ts +0 -25
- package/src/hooks/useRefreshFontScale.ts +0 -39
- package/src/hooks/useThemeState.ts +0 -43
- package/src/index.ts +0 -6
- package/src/store/index.ts +0 -2
- package/src/store/useFontScale.ts +0 -8
- package/src/store/useScreen.ts +0 -25
- package/src/styles/fill.ts +0 -19
- package/src/types/forms.ts +0 -14
- package/src/types/index.ts +0 -1
- package/src/utils/downloadFile.ts +0 -61
- package/src/utils/downloadFileLegacy.ts +0 -66
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
import { useScreen, useToast } from '@chem-po/react'
|
|
2
|
-
import { Ionicons } from '@expo/vector-icons'
|
|
3
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
|
4
|
-
import { Image, Modal, Platform, StyleSheet, View } from 'react-native'
|
|
5
|
-
import {
|
|
6
|
-
Gesture,
|
|
7
|
-
GestureDetector,
|
|
8
|
-
GestureHandlerRootView,
|
|
9
|
-
Pressable,
|
|
10
|
-
} from 'react-native-gesture-handler'
|
|
11
|
-
import Animated, { useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated'
|
|
12
|
-
import { SafeAreaView } from 'react-native-safe-area-context'
|
|
13
|
-
import { downloadFile } from '../../utils/downloadFile'
|
|
14
|
-
import { LoadingLogo } from '../loading/Loading'
|
|
15
|
-
|
|
16
|
-
interface ImageViewModalProps {
|
|
17
|
-
isOpen: boolean
|
|
18
|
-
onClose: () => void
|
|
19
|
-
src: string | null
|
|
20
|
-
filename?: string
|
|
21
|
-
fileType?: string
|
|
22
|
-
withDownload?: boolean
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export const ImageViewModal: React.FC<ImageViewModalProps> = ({
|
|
26
|
-
isOpen,
|
|
27
|
-
onClose,
|
|
28
|
-
src,
|
|
29
|
-
filename,
|
|
30
|
-
fileType,
|
|
31
|
-
withDownload,
|
|
32
|
-
}) => {
|
|
33
|
-
const [loading, setLoading] = useState(true)
|
|
34
|
-
const screenWidth = useScreen(s => s.width)
|
|
35
|
-
const screenHeight = useScreen(s => s.height)
|
|
36
|
-
const [imageSize, setImageSize] = useState({ width: screenWidth / 2, height: screenHeight / 2 })
|
|
37
|
-
const { showError, showSuccess } = useToast()
|
|
38
|
-
|
|
39
|
-
// Shared values for zoom and pan (reanimated)
|
|
40
|
-
const scale = useSharedValue(1)
|
|
41
|
-
const translateX = useSharedValue(0)
|
|
42
|
-
const translateY = useSharedValue(0)
|
|
43
|
-
|
|
44
|
-
// Saved state for gesture handling
|
|
45
|
-
const savedScale = useSharedValue(1)
|
|
46
|
-
const savedTranslateX = useSharedValue(0)
|
|
47
|
-
const savedTranslateY = useSharedValue(0)
|
|
48
|
-
|
|
49
|
-
const { height, width } = useMemo(() => {
|
|
50
|
-
if (loading) return imageSize
|
|
51
|
-
const ratio = imageSize.width / imageSize.height
|
|
52
|
-
let h = Math.min(imageSize.height, screenHeight * 0.9)
|
|
53
|
-
let w = h * ratio
|
|
54
|
-
if (w > screenWidth * 0.9) {
|
|
55
|
-
w = Math.min(imageSize.width, screenWidth * 0.9)
|
|
56
|
-
h = w / ratio
|
|
57
|
-
}
|
|
58
|
-
return { height: h, width: w }
|
|
59
|
-
}, [screenHeight, screenWidth, imageSize, loading])
|
|
60
|
-
|
|
61
|
-
const onLoadStart = useCallback(() => setLoading(true), [])
|
|
62
|
-
const onLoad = useCallback(() => {
|
|
63
|
-
if (!src) return
|
|
64
|
-
Image.getSize(src, (width, height) => {
|
|
65
|
-
setImageSize({ width, height })
|
|
66
|
-
setLoading(false)
|
|
67
|
-
})
|
|
68
|
-
}, [src])
|
|
69
|
-
|
|
70
|
-
// Reset zoom and pan when modal opens/closes
|
|
71
|
-
const resetTransform = useCallback(() => {
|
|
72
|
-
scale.value = 1
|
|
73
|
-
translateX.value = 0
|
|
74
|
-
translateY.value = 0
|
|
75
|
-
savedScale.value = 1
|
|
76
|
-
savedTranslateX.value = 0
|
|
77
|
-
savedTranslateY.value = 0
|
|
78
|
-
}, [scale, translateX, translateY, savedScale, savedTranslateX, savedTranslateY])
|
|
79
|
-
|
|
80
|
-
// Reset when modal closes or src changes
|
|
81
|
-
useEffect(() => {
|
|
82
|
-
if (!isOpen || !src) {
|
|
83
|
-
resetTransform()
|
|
84
|
-
}
|
|
85
|
-
}, [isOpen, src, resetTransform])
|
|
86
|
-
|
|
87
|
-
// Download functionality
|
|
88
|
-
const handleDownload = useCallback(async () => {
|
|
89
|
-
if (!src) {
|
|
90
|
-
showError('Image URL is not available')
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
if (!fileType) {
|
|
94
|
-
showError('File type is not available')
|
|
95
|
-
return
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
const usedName = filename ?? `image_${Date.now()}.jpg`
|
|
100
|
-
|
|
101
|
-
await downloadFile(src, usedName, fileType)
|
|
102
|
-
if (Platform.OS === 'android') {
|
|
103
|
-
showSuccess('Image downloaded successfully')
|
|
104
|
-
return
|
|
105
|
-
}
|
|
106
|
-
} catch (error: unknown) {
|
|
107
|
-
if (error instanceof Error) {
|
|
108
|
-
console.error('Error downloading image:', error.message)
|
|
109
|
-
} else {
|
|
110
|
-
console.error('Error downloading image:', String(error))
|
|
111
|
-
}
|
|
112
|
-
showError('Failed to download image')
|
|
113
|
-
}
|
|
114
|
-
}, [src, showError, showSuccess, filename, fileType])
|
|
115
|
-
|
|
116
|
-
// Pan gesture with worklet
|
|
117
|
-
const panGesture = Gesture.Pan()
|
|
118
|
-
.onUpdate(event => {
|
|
119
|
-
'worklet'
|
|
120
|
-
// Only allow panning if zoomed in
|
|
121
|
-
if (savedScale.value > 1) {
|
|
122
|
-
const newTranslateX = savedTranslateX.value + event.translationX
|
|
123
|
-
const newTranslateY = savedTranslateY.value + event.translationY
|
|
124
|
-
|
|
125
|
-
// Calculate bounds to prevent panning too far
|
|
126
|
-
const maxTranslateX = (width * savedScale.value - width) / 2
|
|
127
|
-
const maxTranslateY = (height * savedScale.value - height) / 2
|
|
128
|
-
|
|
129
|
-
const boundedTranslateX = Math.max(-maxTranslateX, Math.min(maxTranslateX, newTranslateX))
|
|
130
|
-
const boundedTranslateY = Math.max(-maxTranslateY, Math.min(maxTranslateY, newTranslateY))
|
|
131
|
-
|
|
132
|
-
translateX.value = boundedTranslateX
|
|
133
|
-
translateY.value = boundedTranslateY
|
|
134
|
-
}
|
|
135
|
-
})
|
|
136
|
-
.onEnd(() => {
|
|
137
|
-
'worklet'
|
|
138
|
-
savedTranslateX.value = translateX.value
|
|
139
|
-
savedTranslateY.value = translateY.value
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
// Pinch gesture with worklet
|
|
143
|
-
const pinchGesture = Gesture.Pinch()
|
|
144
|
-
.onUpdate(event => {
|
|
145
|
-
'worklet'
|
|
146
|
-
const newScale = savedScale.value * event.scale
|
|
147
|
-
// Limit zoom between 1x and 5x
|
|
148
|
-
const boundedScale = Math.max(1, Math.min(5, newScale))
|
|
149
|
-
scale.value = boundedScale
|
|
150
|
-
|
|
151
|
-
// If zooming out to 1x, reset position
|
|
152
|
-
if (boundedScale <= 1) {
|
|
153
|
-
translateX.value = 0
|
|
154
|
-
translateY.value = 0
|
|
155
|
-
}
|
|
156
|
-
})
|
|
157
|
-
.onEnd(() => {
|
|
158
|
-
'worklet'
|
|
159
|
-
savedScale.value = scale.value
|
|
160
|
-
// If scale is close to 1, snap back to 1
|
|
161
|
-
if (savedScale.value < 1.1) {
|
|
162
|
-
scale.value = withSpring(1)
|
|
163
|
-
translateX.value = withSpring(0)
|
|
164
|
-
translateY.value = withSpring(0)
|
|
165
|
-
savedScale.value = 1
|
|
166
|
-
savedTranslateX.value = 0
|
|
167
|
-
savedTranslateY.value = 0
|
|
168
|
-
} else {
|
|
169
|
-
savedTranslateX.value = translateX.value
|
|
170
|
-
savedTranslateY.value = translateY.value
|
|
171
|
-
}
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
// Double tap to zoom with worklet
|
|
175
|
-
const doubleTapGesture = Gesture.Tap()
|
|
176
|
-
.numberOfTaps(2)
|
|
177
|
-
.onEnd(() => {
|
|
178
|
-
'worklet'
|
|
179
|
-
if (savedScale.value > 1) {
|
|
180
|
-
// Zoom out
|
|
181
|
-
scale.value = withSpring(1)
|
|
182
|
-
translateX.value = withSpring(0)
|
|
183
|
-
translateY.value = withSpring(0)
|
|
184
|
-
savedScale.value = 1
|
|
185
|
-
savedTranslateX.value = 0
|
|
186
|
-
savedTranslateY.value = 0
|
|
187
|
-
} else {
|
|
188
|
-
// Zoom in to 2x
|
|
189
|
-
scale.value = withSpring(2)
|
|
190
|
-
savedScale.value = 2
|
|
191
|
-
}
|
|
192
|
-
})
|
|
193
|
-
|
|
194
|
-
// Combine gestures
|
|
195
|
-
const combinedGestures = Gesture.Race(
|
|
196
|
-
doubleTapGesture,
|
|
197
|
-
Gesture.Simultaneous(panGesture, pinchGesture),
|
|
198
|
-
)
|
|
199
|
-
|
|
200
|
-
// Animated style using reanimated
|
|
201
|
-
const animatedStyle = useAnimatedStyle(() => {
|
|
202
|
-
return {
|
|
203
|
-
transform: [
|
|
204
|
-
{ scale: scale.value },
|
|
205
|
-
{ translateX: translateX.value },
|
|
206
|
-
{ translateY: translateY.value },
|
|
207
|
-
],
|
|
208
|
-
}
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
if (!isOpen) {
|
|
212
|
-
return null
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return (
|
|
216
|
-
<Modal visible={isOpen} transparent animationType="fade" onRequestClose={onClose}>
|
|
217
|
-
<SafeAreaView style={{ flex: 1 }}>
|
|
218
|
-
<GestureHandlerRootView style={styles.gestureRoot}>
|
|
219
|
-
<View style={styles.modalOverlay}>
|
|
220
|
-
<Pressable style={styles.backgroundTouchable} onPress={onClose} />
|
|
221
|
-
<View style={styles.contentContainer}>
|
|
222
|
-
<GestureDetector gesture={combinedGestures}>
|
|
223
|
-
<Animated.View
|
|
224
|
-
style={[
|
|
225
|
-
styles.imageContainer,
|
|
226
|
-
{
|
|
227
|
-
width,
|
|
228
|
-
height,
|
|
229
|
-
opacity: loading ? 0 : 1,
|
|
230
|
-
},
|
|
231
|
-
animatedStyle,
|
|
232
|
-
]}>
|
|
233
|
-
<Image
|
|
234
|
-
source={src ? { uri: src } : undefined}
|
|
235
|
-
style={styles.image}
|
|
236
|
-
onLoadStart={onLoadStart}
|
|
237
|
-
onLoad={onLoad}
|
|
238
|
-
resizeMode="contain"
|
|
239
|
-
/>
|
|
240
|
-
</Animated.View>
|
|
241
|
-
</GestureDetector>
|
|
242
|
-
|
|
243
|
-
{withDownload && (
|
|
244
|
-
<Pressable style={styles.downloadButton} onPress={() => void handleDownload()}>
|
|
245
|
-
<Ionicons name="download" size={24} color="white" />
|
|
246
|
-
</Pressable>
|
|
247
|
-
)}
|
|
248
|
-
|
|
249
|
-
<Pressable style={styles.closeButton} onPress={onClose}>
|
|
250
|
-
<Ionicons name="close" size={24} color="white" />
|
|
251
|
-
</Pressable>
|
|
252
|
-
|
|
253
|
-
{loading || !src ? (
|
|
254
|
-
<View style={styles.loadingContainer}>
|
|
255
|
-
<LoadingLogo isLoading={loading} size={70} />
|
|
256
|
-
</View>
|
|
257
|
-
) : null}
|
|
258
|
-
</View>
|
|
259
|
-
</View>
|
|
260
|
-
</GestureHandlerRootView>
|
|
261
|
-
</SafeAreaView>
|
|
262
|
-
</Modal>
|
|
263
|
-
)
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
const styles = StyleSheet.create({
|
|
267
|
-
gestureRoot: {
|
|
268
|
-
flex: 1,
|
|
269
|
-
},
|
|
270
|
-
modalOverlay: {
|
|
271
|
-
flex: 1,
|
|
272
|
-
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
273
|
-
},
|
|
274
|
-
backgroundTouchable: {
|
|
275
|
-
...StyleSheet.absoluteFillObject,
|
|
276
|
-
},
|
|
277
|
-
contentContainer: {
|
|
278
|
-
flex: 1,
|
|
279
|
-
justifyContent: 'center',
|
|
280
|
-
alignItems: 'center',
|
|
281
|
-
padding: 16,
|
|
282
|
-
},
|
|
283
|
-
imageContainer: {
|
|
284
|
-
overflow: 'hidden',
|
|
285
|
-
borderRadius: 4,
|
|
286
|
-
},
|
|
287
|
-
image: {
|
|
288
|
-
width: '100%',
|
|
289
|
-
height: '100%',
|
|
290
|
-
},
|
|
291
|
-
downloadButton: {
|
|
292
|
-
position: 'absolute',
|
|
293
|
-
top: 16,
|
|
294
|
-
right: 64,
|
|
295
|
-
width: 40,
|
|
296
|
-
height: 40,
|
|
297
|
-
borderRadius: 20,
|
|
298
|
-
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
299
|
-
justifyContent: 'center',
|
|
300
|
-
alignItems: 'center',
|
|
301
|
-
},
|
|
302
|
-
closeButton: {
|
|
303
|
-
position: 'absolute',
|
|
304
|
-
top: 16,
|
|
305
|
-
right: 16,
|
|
306
|
-
width: 40,
|
|
307
|
-
height: 40,
|
|
308
|
-
borderRadius: 20,
|
|
309
|
-
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
|
310
|
-
justifyContent: 'center',
|
|
311
|
-
alignItems: 'center',
|
|
312
|
-
},
|
|
313
|
-
loadingContainer: {
|
|
314
|
-
...StyleSheet.absoluteFillObject,
|
|
315
|
-
pointerEvents: 'none',
|
|
316
|
-
justifyContent: 'center',
|
|
317
|
-
alignItems: 'center',
|
|
318
|
-
},
|
|
319
|
-
})
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './ImageViewModal'
|
package/src/components/index.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import React, { useCallback, useLayoutEffect, useRef, useState } from 'react'
|
|
2
|
-
import { LayoutChangeEvent, StyleProp, View, ViewStyle } from 'react-native'
|
|
3
|
-
import Animated, {
|
|
4
|
-
useAnimatedStyle,
|
|
5
|
-
useSharedValue,
|
|
6
|
-
withSequence,
|
|
7
|
-
withTiming,
|
|
8
|
-
} from 'react-native-reanimated'
|
|
9
|
-
|
|
10
|
-
export const CollapseHorizontal = ({
|
|
11
|
-
children,
|
|
12
|
-
in: isIn,
|
|
13
|
-
duration = 200,
|
|
14
|
-
style,
|
|
15
|
-
}: {
|
|
16
|
-
children: React.ReactNode
|
|
17
|
-
in: boolean
|
|
18
|
-
duration?: number
|
|
19
|
-
style?: StyleProp<ViewStyle>
|
|
20
|
-
}) => {
|
|
21
|
-
const contentRef = useRef<View>(null)
|
|
22
|
-
const [contentWidth, setContentWidth] = useState(0)
|
|
23
|
-
const width = useSharedValue(0)
|
|
24
|
-
const opacity = useSharedValue(0)
|
|
25
|
-
const handleLayout = useCallback(
|
|
26
|
-
(event: LayoutChangeEvent) => {
|
|
27
|
-
setContentWidth(event.nativeEvent.layout.width)
|
|
28
|
-
// Initialize width if component is initially visible
|
|
29
|
-
if (isIn && width.value === 0) {
|
|
30
|
-
width.value = event.nativeEvent.layout.width
|
|
31
|
-
opacity.value = 1
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
[isIn, width, opacity],
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
useLayoutEffect(() => {
|
|
38
|
-
if (contentRef.current) {
|
|
39
|
-
contentRef.current.measure((_x, _y, width) => {
|
|
40
|
-
setContentWidth(width)
|
|
41
|
-
})
|
|
42
|
-
}
|
|
43
|
-
}, [])
|
|
44
|
-
|
|
45
|
-
React.useEffect(() => {
|
|
46
|
-
if (isIn) {
|
|
47
|
-
// Fade in: width first, then opacity
|
|
48
|
-
width.value = withTiming(contentWidth, {
|
|
49
|
-
duration,
|
|
50
|
-
})
|
|
51
|
-
opacity.value = withSequence(
|
|
52
|
-
withTiming(0, { duration: 0 }), // Reset opacity
|
|
53
|
-
withTiming(1, {
|
|
54
|
-
duration,
|
|
55
|
-
}),
|
|
56
|
-
)
|
|
57
|
-
} else {
|
|
58
|
-
// Fade out: opacity first, then width
|
|
59
|
-
opacity.value = withTiming(0, {
|
|
60
|
-
duration,
|
|
61
|
-
})
|
|
62
|
-
width.value = withSequence(
|
|
63
|
-
withTiming(width.value, { duration: 0 }), // Ensure we start from current width
|
|
64
|
-
withTiming(0, {
|
|
65
|
-
duration,
|
|
66
|
-
}),
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
}, [isIn, contentWidth, duration, width, opacity])
|
|
70
|
-
|
|
71
|
-
const animatedStyle = useAnimatedStyle(() => ({
|
|
72
|
-
width: width.value,
|
|
73
|
-
opacity: opacity.value,
|
|
74
|
-
}))
|
|
75
|
-
|
|
76
|
-
return (
|
|
77
|
-
<Animated.View
|
|
78
|
-
style={[
|
|
79
|
-
{
|
|
80
|
-
overflow: 'hidden',
|
|
81
|
-
pointerEvents: isIn ? 'auto' : 'none',
|
|
82
|
-
},
|
|
83
|
-
animatedStyle,
|
|
84
|
-
]}>
|
|
85
|
-
<View style={{ position: 'absolute', width: 'auto' }}>
|
|
86
|
-
<View style={style} onLayout={handleLayout} ref={contentRef}>
|
|
87
|
-
{children}
|
|
88
|
-
</View>
|
|
89
|
-
</View>
|
|
90
|
-
</Animated.View>
|
|
91
|
-
)
|
|
92
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { useBackgroundColor, useColorModeValue } from '@chem-po/react'
|
|
2
|
-
import React, { useEffect, useRef } from 'react'
|
|
3
|
-
import {
|
|
4
|
-
ActivityIndicator,
|
|
5
|
-
ActivityIndicatorProps,
|
|
6
|
-
Animated,
|
|
7
|
-
StyleProp,
|
|
8
|
-
StyleSheet,
|
|
9
|
-
ViewStyle,
|
|
10
|
-
} from 'react-native'
|
|
11
|
-
|
|
12
|
-
export interface CircularProgressProps extends ActivityIndicatorProps {
|
|
13
|
-
trackColor?: string
|
|
14
|
-
viewStyle?: StyleProp<ViewStyle>
|
|
15
|
-
visible?: boolean
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const CircularProgress = ({
|
|
19
|
-
color,
|
|
20
|
-
trackColor,
|
|
21
|
-
animating,
|
|
22
|
-
visible = true,
|
|
23
|
-
viewStyle,
|
|
24
|
-
...props
|
|
25
|
-
}: CircularProgressProps) => {
|
|
26
|
-
const defaultTrackColor = useColorModeValue('#959595', '#575757')
|
|
27
|
-
const defaultColor = useBackgroundColor(750)
|
|
28
|
-
const opacity = useRef(new Animated.Value(visible ? 1 : 0)).current
|
|
29
|
-
|
|
30
|
-
useEffect(() => {
|
|
31
|
-
Animated.timing(opacity, {
|
|
32
|
-
toValue: visible ? 1 : 0,
|
|
33
|
-
duration: 300,
|
|
34
|
-
useNativeDriver: true,
|
|
35
|
-
}).start()
|
|
36
|
-
}, [visible, opacity])
|
|
37
|
-
return (
|
|
38
|
-
<Animated.View style={[styles.container, { opacity }, viewStyle]}>
|
|
39
|
-
<ActivityIndicator
|
|
40
|
-
color={trackColor ?? defaultTrackColor} // Lighter version of your color
|
|
41
|
-
style={StyleSheet.absoluteFill}
|
|
42
|
-
animating={false}
|
|
43
|
-
{...props}
|
|
44
|
-
/>
|
|
45
|
-
{/* Spinning circle */}
|
|
46
|
-
<ActivityIndicator animating={animating} color={color ?? defaultColor} {...props} />
|
|
47
|
-
</Animated.View>
|
|
48
|
-
)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const styles = StyleSheet.create({
|
|
52
|
-
container: {
|
|
53
|
-
alignItems: 'center',
|
|
54
|
-
justifyContent: 'center',
|
|
55
|
-
},
|
|
56
|
-
})
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { ThemedAsset, useAppAssets, useTheme } from '@chem-po/react'
|
|
2
|
-
import LottieView from 'lottie-react-native'
|
|
3
|
-
import React, { useMemo } from 'react'
|
|
4
|
-
import { Image, StyleSheet, Text, View, ViewStyle } from 'react-native'
|
|
5
|
-
|
|
6
|
-
export const LottieLoadingLogo = ({
|
|
7
|
-
size = 30,
|
|
8
|
-
isLoading,
|
|
9
|
-
speed = 2,
|
|
10
|
-
inFeed,
|
|
11
|
-
asset,
|
|
12
|
-
}: {
|
|
13
|
-
size?: number
|
|
14
|
-
color?: string
|
|
15
|
-
isLoading: boolean
|
|
16
|
-
asset: ThemedAsset
|
|
17
|
-
inFeed?: boolean
|
|
18
|
-
speed?: number
|
|
19
|
-
}) => {
|
|
20
|
-
const { colorMode } = useTheme()
|
|
21
|
-
const animationData = useMemo(
|
|
22
|
-
() =>
|
|
23
|
-
inFeed
|
|
24
|
-
? (asset.dark ?? asset.default)
|
|
25
|
-
: colorMode === 'dark'
|
|
26
|
-
? (asset.dark ?? asset.default)
|
|
27
|
-
: asset.default,
|
|
28
|
-
[asset, inFeed, colorMode],
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
const style = useMemo(
|
|
32
|
-
() => ({
|
|
33
|
-
width: size,
|
|
34
|
-
height: size,
|
|
35
|
-
opacity: isLoading ? 0.85 : 0,
|
|
36
|
-
}),
|
|
37
|
-
[size, isLoading],
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
return <LottieView source={animationData} autoPlay={isLoading} loop speed={speed} style={style} />
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const SVGLoadingLogo = ({
|
|
44
|
-
size = 30,
|
|
45
|
-
isLoading,
|
|
46
|
-
asset,
|
|
47
|
-
inFeed,
|
|
48
|
-
}: {
|
|
49
|
-
size?: number
|
|
50
|
-
isLoading: boolean
|
|
51
|
-
asset: ThemedAsset
|
|
52
|
-
inFeed?: boolean
|
|
53
|
-
}) => {
|
|
54
|
-
const { colorMode } = useTheme()
|
|
55
|
-
const svg = useMemo(
|
|
56
|
-
() =>
|
|
57
|
-
inFeed
|
|
58
|
-
? (asset.dark ?? asset.default)
|
|
59
|
-
: colorMode === 'dark'
|
|
60
|
-
? (asset.dark ?? asset.default)
|
|
61
|
-
: asset.default,
|
|
62
|
-
[asset, inFeed, colorMode],
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
const style = useMemo(
|
|
66
|
-
() => ({
|
|
67
|
-
width: size,
|
|
68
|
-
height: size,
|
|
69
|
-
opacity: isLoading ? 0.85 : 0,
|
|
70
|
-
}),
|
|
71
|
-
[size, isLoading],
|
|
72
|
-
)
|
|
73
|
-
|
|
74
|
-
return <Image source={{ uri: svg }} style={style} />
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const LoadingBody = ({ isLoading, size }: { isLoading: boolean; size?: number }) => {
|
|
78
|
-
const { loading } = useAppAssets()
|
|
79
|
-
if (loading.lottieJson)
|
|
80
|
-
return <LottieLoadingLogo isLoading={isLoading} size={size} asset={loading.lottieJson} />
|
|
81
|
-
if (loading.svg) return <SVGLoadingLogo isLoading={isLoading} size={size} asset={loading.svg} />
|
|
82
|
-
return null
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export const LoadingLogo = ({
|
|
86
|
-
isLoading,
|
|
87
|
-
size,
|
|
88
|
-
inFeed,
|
|
89
|
-
}: {
|
|
90
|
-
isLoading: boolean
|
|
91
|
-
size?: number
|
|
92
|
-
inFeed?: boolean
|
|
93
|
-
}) => {
|
|
94
|
-
const { loading } = useAppAssets()
|
|
95
|
-
if (loading.lottieJson)
|
|
96
|
-
return (
|
|
97
|
-
<LottieLoadingLogo
|
|
98
|
-
inFeed={inFeed}
|
|
99
|
-
isLoading={isLoading}
|
|
100
|
-
size={size}
|
|
101
|
-
asset={loading.lottieJson}
|
|
102
|
-
/>
|
|
103
|
-
)
|
|
104
|
-
if (loading.svg)
|
|
105
|
-
return <SVGLoadingLogo inFeed={inFeed} isLoading={isLoading} size={size} asset={loading.svg} />
|
|
106
|
-
return <Text style={styles.errorText}>ERROR: No loading animation found</Text>
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export const Loading = ({
|
|
110
|
-
text = 'Loading...',
|
|
111
|
-
inBox,
|
|
112
|
-
containerStyle,
|
|
113
|
-
}: {
|
|
114
|
-
text?: string
|
|
115
|
-
inBox?: boolean
|
|
116
|
-
containerStyle?: ViewStyle
|
|
117
|
-
}) => {
|
|
118
|
-
const body = (
|
|
119
|
-
<View style={[styles.loadingContainer, containerStyle]}>
|
|
120
|
-
<LoadingBody isLoading />
|
|
121
|
-
<Text style={styles.loadingText}>{text}</Text>
|
|
122
|
-
</View>
|
|
123
|
-
)
|
|
124
|
-
return inBox ? <View style={styles.boxContainer}>{body}</View> : body
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const styles = StyleSheet.create({
|
|
128
|
-
loadingContainer: {
|
|
129
|
-
flexDirection: 'row',
|
|
130
|
-
alignItems: 'center',
|
|
131
|
-
padding: 8,
|
|
132
|
-
},
|
|
133
|
-
loadingText: {
|
|
134
|
-
opacity: 0.8,
|
|
135
|
-
fontSize: 14,
|
|
136
|
-
marginLeft: 8,
|
|
137
|
-
},
|
|
138
|
-
boxContainer: {
|
|
139
|
-
padding: 16,
|
|
140
|
-
borderRadius: 8,
|
|
141
|
-
backgroundColor: 'rgba(0, 0, 0, 0.05)',
|
|
142
|
-
},
|
|
143
|
-
errorText: {
|
|
144
|
-
color: 'red',
|
|
145
|
-
},
|
|
146
|
-
})
|