@chem-po/react-native 0.0.30 → 0.0.32

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 (154) hide show
  1. package/lib/commonjs/components/button/DeleteButton.js +7 -8
  2. package/lib/commonjs/components/button/DeleteButton.js.map +1 -1
  3. package/lib/commonjs/components/button/LoadingButton.js +3 -3
  4. package/lib/commonjs/components/button/LoadingButton.js.map +1 -1
  5. package/lib/commonjs/components/button/Toggle.js +2 -1
  6. package/lib/commonjs/components/button/Toggle.js.map +1 -1
  7. package/lib/commonjs/components/form/Form.js +30 -4
  8. package/lib/commonjs/components/form/Form.js.map +1 -1
  9. package/lib/commonjs/components/form/FormFooter.js +3 -2
  10. package/lib/commonjs/components/form/FormFooter.js.map +1 -1
  11. package/lib/commonjs/components/form/input/Editable.js +4 -6
  12. package/lib/commonjs/components/form/input/Editable.js.map +1 -1
  13. package/lib/commonjs/components/form/input/common/InputClearButton.js +2 -1
  14. package/lib/commonjs/components/form/input/common/InputClearButton.js.map +1 -1
  15. package/lib/commonjs/components/form/input/date/index.js +23 -10
  16. package/lib/commonjs/components/form/input/date/index.js.map +1 -1
  17. package/lib/commonjs/components/form/input/datetime/index.js +27 -11
  18. package/lib/commonjs/components/form/input/datetime/index.js.map +1 -1
  19. package/lib/commonjs/components/form/input/file/index.js +31 -28
  20. package/lib/commonjs/components/form/input/file/index.js.map +1 -1
  21. package/lib/commonjs/components/form/input/hooks/useInputStyles.js +5 -6
  22. package/lib/commonjs/components/form/input/hooks/useInputStyles.js.map +1 -1
  23. package/lib/commonjs/components/form/input/input.js +3 -3
  24. package/lib/commonjs/components/form/input/input.js.map +1 -1
  25. package/lib/commonjs/components/form/input/multipleSelect/index.js +35 -23
  26. package/lib/commonjs/components/form/input/multipleSelect/index.js.map +1 -1
  27. package/lib/commonjs/components/form/input/socialMedia/index.js +3 -2
  28. package/lib/commonjs/components/form/input/socialMedia/index.js.map +1 -1
  29. package/lib/commonjs/components/form/input/text/AutoResizeTextarea.js +1 -6
  30. package/lib/commonjs/components/form/input/text/AutoResizeTextarea.js.map +1 -1
  31. package/lib/commonjs/components/form/input/text/index.js +2 -1
  32. package/lib/commonjs/components/form/input/text/index.js.map +1 -1
  33. package/lib/commonjs/components/form/input/text/textarea.js +2 -1
  34. package/lib/commonjs/components/form/input/text/textarea.js.map +1 -1
  35. package/lib/commonjs/components/form/input/text/useWebAutoResize.js +4 -4
  36. package/lib/commonjs/components/form/input/text/useWebAutoResize.js.map +1 -1
  37. package/lib/commonjs/components/form/input/time/index.js +2 -1
  38. package/lib/commonjs/components/form/input/time/index.js.map +1 -1
  39. package/lib/commonjs/components/form/view/select.js +6 -2
  40. package/lib/commonjs/components/form/view/select.js.map +1 -1
  41. package/lib/commonjs/components/image/ImageViewModal.js +22 -16
  42. package/lib/commonjs/components/image/ImageViewModal.js.map +1 -1
  43. package/lib/commonjs/components/layout/CollapseHorizontal.js +23 -11
  44. package/lib/commonjs/components/layout/CollapseHorizontal.js.map +1 -1
  45. package/lib/commonjs/components/loading/LoadingImage.js +20 -13
  46. package/lib/commonjs/components/loading/LoadingImage.js.map +1 -1
  47. package/lib/commonjs/components/theme/colorMode/DarkModeToggle.js +2 -1
  48. package/lib/commonjs/components/theme/colorMode/DarkModeToggle.js.map +1 -1
  49. package/lib/commonjs/utils/downloadFile.js +36 -26
  50. package/lib/commonjs/utils/downloadFile.js.map +1 -1
  51. package/lib/module/components/button/DeleteButton.js +8 -9
  52. package/lib/module/components/button/DeleteButton.js.map +1 -1
  53. package/lib/module/components/button/LoadingButton.js +4 -4
  54. package/lib/module/components/button/LoadingButton.js.map +1 -1
  55. package/lib/module/components/button/Toggle.js +3 -2
  56. package/lib/module/components/button/Toggle.js.map +1 -1
  57. package/lib/module/components/form/Form.js +31 -5
  58. package/lib/module/components/form/Form.js.map +1 -1
  59. package/lib/module/components/form/FormFooter.js +4 -3
  60. package/lib/module/components/form/FormFooter.js.map +1 -1
  61. package/lib/module/components/form/input/Editable.js +5 -7
  62. package/lib/module/components/form/input/Editable.js.map +1 -1
  63. package/lib/module/components/form/input/common/InputClearButton.js +3 -2
  64. package/lib/module/components/form/input/common/InputClearButton.js.map +1 -1
  65. package/lib/module/components/form/input/date/index.js +24 -11
  66. package/lib/module/components/form/input/date/index.js.map +1 -1
  67. package/lib/module/components/form/input/datetime/index.js +29 -13
  68. package/lib/module/components/form/input/datetime/index.js.map +1 -1
  69. package/lib/module/components/form/input/file/index.js +33 -30
  70. package/lib/module/components/form/input/file/index.js.map +1 -1
  71. package/lib/module/components/form/input/hooks/useInputStyles.js +5 -6
  72. package/lib/module/components/form/input/hooks/useInputStyles.js.map +1 -1
  73. package/lib/module/components/form/input/input.js +3 -3
  74. package/lib/module/components/form/input/input.js.map +1 -1
  75. package/lib/module/components/form/input/multipleSelect/index.js +35 -23
  76. package/lib/module/components/form/input/multipleSelect/index.js.map +1 -1
  77. package/lib/module/components/form/input/socialMedia/index.js +4 -3
  78. package/lib/module/components/form/input/socialMedia/index.js.map +1 -1
  79. package/lib/module/components/form/input/text/AutoResizeTextarea.js +1 -6
  80. package/lib/module/components/form/input/text/AutoResizeTextarea.js.map +1 -1
  81. package/lib/module/components/form/input/text/index.js +3 -2
  82. package/lib/module/components/form/input/text/index.js.map +1 -1
  83. package/lib/module/components/form/input/text/textarea.js +2 -1
  84. package/lib/module/components/form/input/text/textarea.js.map +1 -1
  85. package/lib/module/components/form/input/text/useWebAutoResize.js +4 -4
  86. package/lib/module/components/form/input/text/useWebAutoResize.js.map +1 -1
  87. package/lib/module/components/form/input/time/index.js +3 -2
  88. package/lib/module/components/form/input/time/index.js.map +1 -1
  89. package/lib/module/components/form/view/select.js +6 -2
  90. package/lib/module/components/form/view/select.js.map +1 -1
  91. package/lib/module/components/image/ImageViewModal.js +24 -18
  92. package/lib/module/components/image/ImageViewModal.js.map +1 -1
  93. package/lib/module/components/layout/CollapseHorizontal.js +24 -12
  94. package/lib/module/components/layout/CollapseHorizontal.js.map +1 -1
  95. package/lib/module/components/loading/LoadingImage.js +21 -14
  96. package/lib/module/components/loading/LoadingImage.js.map +1 -1
  97. package/lib/module/components/theme/colorMode/DarkModeToggle.js +3 -2
  98. package/lib/module/components/theme/colorMode/DarkModeToggle.js.map +1 -1
  99. package/lib/module/utils/downloadFile.js +35 -25
  100. package/lib/module/utils/downloadFile.js.map +1 -1
  101. package/lib/typescript/components/button/DeleteButton.d.ts.map +1 -1
  102. package/lib/typescript/components/button/LoadingButton.d.ts.map +1 -1
  103. package/lib/typescript/components/button/Toggle.d.ts.map +1 -1
  104. package/lib/typescript/components/form/Form.d.ts.map +1 -1
  105. package/lib/typescript/components/form/FormFooter.d.ts.map +1 -1
  106. package/lib/typescript/components/form/input/Editable.d.ts.map +1 -1
  107. package/lib/typescript/components/form/input/common/InputClearButton.d.ts.map +1 -1
  108. package/lib/typescript/components/form/input/date/index.d.ts.map +1 -1
  109. package/lib/typescript/components/form/input/datetime/index.d.ts.map +1 -1
  110. package/lib/typescript/components/form/input/file/index.d.ts +2 -1
  111. package/lib/typescript/components/form/input/file/index.d.ts.map +1 -1
  112. package/lib/typescript/components/form/input/hooks/useInputStyles.d.ts +3 -6
  113. package/lib/typescript/components/form/input/hooks/useInputStyles.d.ts.map +1 -1
  114. package/lib/typescript/components/form/input/multipleSelect/index.d.ts.map +1 -1
  115. package/lib/typescript/components/form/input/socialMedia/index.d.ts.map +1 -1
  116. package/lib/typescript/components/form/input/text/AutoResizeTextarea.d.ts.map +1 -1
  117. package/lib/typescript/components/form/input/text/index.d.ts.map +1 -1
  118. package/lib/typescript/components/form/input/text/useWebAutoResize.d.ts +2 -3
  119. package/lib/typescript/components/form/input/text/useWebAutoResize.d.ts.map +1 -1
  120. package/lib/typescript/components/form/input/time/index.d.ts.map +1 -1
  121. package/lib/typescript/components/form/view/select.d.ts.map +1 -1
  122. package/lib/typescript/components/image/ImageViewModal.d.ts.map +1 -1
  123. package/lib/typescript/components/layout/CollapseHorizontal.d.ts.map +1 -1
  124. package/lib/typescript/components/loading/LoadingImage.d.ts +4 -1
  125. package/lib/typescript/components/loading/LoadingImage.d.ts.map +1 -1
  126. package/lib/typescript/components/theme/colorMode/DarkModeToggle.d.ts.map +1 -1
  127. package/lib/typescript/utils/downloadFile.d.ts +2 -2
  128. package/lib/typescript/utils/downloadFile.d.ts.map +1 -1
  129. package/package.json +5 -4
  130. package/src/components/button/DeleteButton.tsx +12 -25
  131. package/src/components/button/LoadingButton.tsx +4 -5
  132. package/src/components/button/Toggle.tsx +4 -3
  133. package/src/components/form/Form.tsx +31 -11
  134. package/src/components/form/FormFooter.tsx +6 -5
  135. package/src/components/form/input/Editable.tsx +8 -9
  136. package/src/components/form/input/common/InputClearButton.tsx +4 -3
  137. package/src/components/form/input/date/index.tsx +24 -11
  138. package/src/components/form/input/datetime/index.tsx +33 -16
  139. package/src/components/form/input/file/index.tsx +38 -36
  140. package/src/components/form/input/hooks/useInputStyles.ts +10 -7
  141. package/src/components/form/input/input.tsx +2 -2
  142. package/src/components/form/input/multipleSelect/index.tsx +35 -35
  143. package/src/components/form/input/socialMedia/index.tsx +6 -5
  144. package/src/components/form/input/text/AutoResizeTextarea.tsx +1 -5
  145. package/src/components/form/input/text/index.tsx +4 -3
  146. package/src/components/form/input/text/textarea.tsx +1 -1
  147. package/src/components/form/input/text/useWebAutoResize.tsx +6 -4
  148. package/src/components/form/input/time/index.tsx +4 -3
  149. package/src/components/form/view/select.tsx +5 -1
  150. package/src/components/image/ImageViewModal.tsx +60 -51
  151. package/src/components/layout/CollapseHorizontal.tsx +20 -10
  152. package/src/components/loading/LoadingImage.tsx +19 -19
  153. package/src/components/theme/colorMode/DarkModeToggle.tsx +4 -3
  154. package/src/utils/downloadFile.ts +41 -31
@@ -2,7 +2,8 @@ import { InputRef } from '@chem-po/core'
2
2
  import { TextField, usePlaceholderColor, useTextColor } from '@chem-po/react'
3
3
  import { Ionicons } from '@expo/vector-icons'
4
4
  import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react'
5
- import { StyleSheet, TextInput, TouchableOpacity, View } from 'react-native'
5
+ import { StyleSheet, TextInput, View } from 'react-native'
6
+ import { Pressable } from 'react-native-gesture-handler'
6
7
  import { FieldProps } from '../../types'
7
8
  import { useInputStyles } from '../hooks/useInputStyles'
8
9
  import { TextAreaComponent } from './textarea'
@@ -63,9 +64,9 @@ export const TextComponent = forwardRef<InputRef, FieldProps<TextField>>(
63
64
  return type === 'password' ? (
64
65
  <View style={[conatinerStyles, styles.passwordContainer, style]}>
65
66
  {body}
66
- <TouchableOpacity style={styles.eyeIcon} onPress={() => setIsHidden(!isHidden)}>
67
+ <Pressable style={styles.eyeIcon} onPress={() => setIsHidden(!isHidden)}>
67
68
  <Ionicons name={isHidden ? 'eye' : 'eye-off'} size={20} color="#666" />
68
- </TouchableOpacity>
69
+ </Pressable>
69
70
  </View>
70
71
  ) : (
71
72
  <View style={[conatinerStyles, style]}>{body}</View>
@@ -16,7 +16,7 @@ export const TextAreaComponent = forwardRef<TextInput, FieldProps<TextField>>(
16
16
  <View style={[container, style]}>
17
17
  <AutoResizeTextarea
18
18
  ref={ref}
19
- style={[{ color, ...text }, inputStyle]}
19
+ style={[{ color, ...text, paddingBottom: inEditable ? 0 : 4 }, inputStyle]}
20
20
  placeholder={field.placeholder}
21
21
  placeholderTextColor={placeholderColor}
22
22
  multiline
@@ -39,7 +39,10 @@ export const useWebAutoResize = (
39
39
  style: StyleProp<TextStyle> | undefined,
40
40
  ) => {
41
41
  const flattenedStyle = useMemo(() => StyleSheet.flatten(style), [style])
42
- const [height, setHeight] = useState((flattenedStyle.fontSize ?? 18) + 4)
42
+ const fontSize = useMemo(() => flattenedStyle.fontSize ?? 16, [flattenedStyle.fontSize])
43
+ const [height, setHeight] = useState<number | 'auto'>(
44
+ Platform.OS === 'web' ? fontSize + 10 : 'auto',
45
+ )
43
46
  const spanRef = useRef<HTMLSpanElement>(
44
47
  Platform.OS === 'web' ? document.createElement('span') : null,
45
48
  )
@@ -55,15 +58,14 @@ export const useWebAutoResize = (
55
58
  ? (e: string) => {
56
59
  if (spanRef.current) {
57
60
  const height = getWebTextHeight(e, spanRef)
58
- setHeight((height ?? 18) + 4)
61
+ setHeight((height ?? fontSize) + 8)
59
62
  if (onChangeText) onChangeText(e)
60
63
  }
61
64
  }
62
65
  : onChangeText,
63
- [onChangeText],
66
+ [onChangeText, fontSize],
64
67
  )
65
68
  return {
66
- getWebTextHeight,
67
69
  handleChange,
68
70
  height,
69
71
  setHeight,
@@ -2,7 +2,8 @@ import { InputRef, padZeros } from '@chem-po/core'
2
2
  import { TimeField, useIconColor } from '@chem-po/react'
3
3
  import { Ionicons } from '@expo/vector-icons'
4
4
  import React, { forwardRef, useImperativeHandle, useMemo, useState } from 'react'
5
- import { StyleSheet, Text, TouchableOpacity, View } from 'react-native'
5
+ import { StyleSheet, Text, View } from 'react-native'
6
+ import { Pressable } from 'react-native-gesture-handler'
6
7
  import { Portal } from 'react-native-paper'
7
8
  import { TimePickerModal } from 'react-native-paper-dates'
8
9
  import { FieldProps } from '../../types'
@@ -71,7 +72,7 @@ export const TimeInput = forwardRef<InputRef, FieldProps<TimeField>>(
71
72
 
72
73
  return (
73
74
  <View style={styles.container}>
74
- <TouchableOpacity
75
+ <Pressable
75
76
  style={[styles.button, inputStyles]}
76
77
  onPress={() => {
77
78
  setVisible(true)
@@ -90,7 +91,7 @@ export const TimeInput = forwardRef<InputRef, FieldProps<TimeField>>(
90
91
  />
91
92
  ) : null}
92
93
  </View>
93
- </TouchableOpacity>
94
+ </Pressable>
94
95
  <Portal>
95
96
  <TimePickerModal
96
97
  visible={visible}
@@ -24,7 +24,11 @@ export const DefaultRenderOption = <
24
24
  >({
25
25
  option,
26
26
  }: RenderSelectOptionProps<Value, Option>) => {
27
- return <Txt style={inputViewStyles.value}>{option.label}</Txt>
27
+ return (
28
+ <View style={{ padding: 6 }}>
29
+ <Txt style={inputViewStyles.value}>{option.label}</Txt>
30
+ </View>
31
+ )
28
32
  }
29
33
 
30
34
  export const SelectFieldView = ({
@@ -1,8 +1,13 @@
1
1
  import { useScreen, useToast } from '@chem-po/react'
2
2
  import { Ionicons } from '@expo/vector-icons'
3
3
  import React, { useCallback, useEffect, useMemo, useState } from 'react'
4
- import { Image, Modal, StyleSheet, TouchableOpacity, View } from 'react-native'
5
- import { Gesture, GestureDetector, GestureHandlerRootView } from 'react-native-gesture-handler'
4
+ import { Image, Modal, Platform, SafeAreaView, StyleSheet, View } from 'react-native'
5
+ import {
6
+ Gesture,
7
+ GestureDetector,
8
+ GestureHandlerRootView,
9
+ Pressable,
10
+ } from 'react-native-gesture-handler'
6
11
  import Animated, { useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated'
7
12
  import { downloadFile } from '../../utils/downloadFile'
8
13
  import { LoadingLogo } from '../loading/Loading'
@@ -53,11 +58,13 @@ export const ImageViewModal: React.FC<ImageViewModalProps> = ({
53
58
  }, [screenHeight, screenWidth, imageSize, loading])
54
59
 
55
60
  const onLoadStart = useCallback(() => setLoading(true), [])
56
- const onLoad = useCallback((e: any) => {
57
- const { width: naturalWidth, height: naturalHeight } = e.nativeEvent.source
58
- setImageSize({ width: naturalWidth, height: naturalHeight })
59
- setLoading(false)
60
- }, [])
61
+ const onLoad = useCallback(() => {
62
+ if (!src) return
63
+ Image.getSize(src, (width, height) => {
64
+ setImageSize({ width, height })
65
+ setLoading(false)
66
+ })
67
+ }, [src])
61
68
 
62
69
  // Reset zoom and pan when modal opens/closes
63
70
  const resetTransform = useCallback(() => {
@@ -91,7 +98,10 @@ export const ImageViewModal: React.FC<ImageViewModalProps> = ({
91
98
  const usedName = filename ?? `image_${Date.now()}.jpg`
92
99
 
93
100
  await downloadFile(src, usedName, fileType)
94
- showSuccess('Image downloaded successfully')
101
+ if (Platform.OS === 'android') {
102
+ showSuccess('Image downloaded successfully')
103
+ return
104
+ }
95
105
  } catch (error: unknown) {
96
106
  if (error instanceof Error) {
97
107
  console.error('Error downloading image:', error.message)
@@ -203,53 +213,51 @@ export const ImageViewModal: React.FC<ImageViewModalProps> = ({
203
213
 
204
214
  return (
205
215
  <Modal visible={isOpen} transparent animationType="fade" onRequestClose={onClose}>
206
- <GestureHandlerRootView style={styles.gestureRoot}>
207
- <View style={styles.modalOverlay}>
208
- <TouchableOpacity
209
- style={styles.backgroundTouchable}
210
- activeOpacity={1}
211
- onPress={onClose}
212
- />
213
- <View style={styles.contentContainer}>
214
- <GestureDetector gesture={combinedGestures}>
215
- <Animated.View
216
- style={[
217
- styles.imageContainer,
218
- {
219
- width,
220
- height,
221
- opacity: loading ? 0 : 1,
222
- },
223
- animatedStyle,
224
- ]}>
225
- <Image
226
- source={src ? { uri: src } : undefined}
227
- style={styles.image}
228
- onLoadStart={onLoadStart}
229
- onLoad={onLoad}
230
- resizeMode="contain"
231
- />
232
- </Animated.View>
233
- </GestureDetector>
216
+ <SafeAreaView style={{ flex: 1 }}>
217
+ <GestureHandlerRootView style={styles.gestureRoot}>
218
+ <View style={styles.modalOverlay}>
219
+ <Pressable style={styles.backgroundTouchable} onPress={onClose} />
220
+ <View style={styles.contentContainer}>
221
+ <GestureDetector gesture={combinedGestures}>
222
+ <Animated.View
223
+ style={[
224
+ styles.imageContainer,
225
+ {
226
+ width,
227
+ height,
228
+ opacity: loading ? 0 : 1,
229
+ },
230
+ animatedStyle,
231
+ ]}>
232
+ <Image
233
+ source={src ? { uri: src } : undefined}
234
+ style={styles.image}
235
+ onLoadStart={onLoadStart}
236
+ onLoad={onLoad}
237
+ resizeMode="contain"
238
+ />
239
+ </Animated.View>
240
+ </GestureDetector>
234
241
 
235
- {withDownload && (
236
- <TouchableOpacity style={styles.downloadButton} onPress={() => void handleDownload()}>
237
- <Ionicons name="download" size={24} color="white" />
238
- </TouchableOpacity>
239
- )}
242
+ {withDownload && (
243
+ <Pressable style={styles.downloadButton} onPress={() => void handleDownload()}>
244
+ <Ionicons name="download" size={24} color="white" />
245
+ </Pressable>
246
+ )}
240
247
 
241
- <TouchableOpacity style={styles.closeButton} onPress={onClose}>
242
- <Ionicons name="close" size={24} color="white" />
243
- </TouchableOpacity>
248
+ <Pressable style={styles.closeButton} onPress={onClose}>
249
+ <Ionicons name="close" size={24} color="white" />
250
+ </Pressable>
244
251
 
245
- {loading || !src ? (
246
- <View style={styles.loadingContainer}>
247
- <LoadingLogo isLoading={loading} size={70} />
248
- </View>
249
- ) : null}
252
+ {loading || !src ? (
253
+ <View style={styles.loadingContainer}>
254
+ <LoadingLogo isLoading={loading} size={70} />
255
+ </View>
256
+ ) : null}
257
+ </View>
250
258
  </View>
251
- </View>
252
- </GestureHandlerRootView>
259
+ </GestureHandlerRootView>
260
+ </SafeAreaView>
253
261
  </Modal>
254
262
  )
255
263
  }
@@ -303,6 +311,7 @@ const styles = StyleSheet.create({
303
311
  },
304
312
  loadingContainer: {
305
313
  ...StyleSheet.absoluteFillObject,
314
+ pointerEvents: 'none',
306
315
  justifyContent: 'center',
307
316
  alignItems: 'center',
308
317
  },
@@ -1,4 +1,4 @@
1
- import React, { useCallback } from 'react'
1
+ import React, { useCallback, useLayoutEffect, useRef, useState } from 'react'
2
2
  import { LayoutChangeEvent, StyleProp, View, ViewStyle } from 'react-native'
3
3
  import Animated, {
4
4
  useAnimatedStyle,
@@ -18,26 +18,34 @@ export const CollapseHorizontal = ({
18
18
  duration?: number
19
19
  style?: StyleProp<ViewStyle>
20
20
  }) => {
21
- const contentWidth = useSharedValue(0)
21
+ const contentRef = useRef<View>(null)
22
+ const [contentWidth, setContentWidth] = useState(0)
22
23
  const width = useSharedValue(0)
23
24
  const opacity = useSharedValue(0)
24
-
25
25
  const handleLayout = useCallback(
26
26
  (event: LayoutChangeEvent) => {
27
- contentWidth.value = event.nativeEvent.layout.width
27
+ setContentWidth(event.nativeEvent.layout.width)
28
28
  // Initialize width if component is initially visible
29
29
  if (isIn && width.value === 0) {
30
- width.value = contentWidth.value
30
+ width.value = event.nativeEvent.layout.width
31
31
  opacity.value = 1
32
32
  }
33
33
  },
34
- [isIn, contentWidth, width, opacity],
34
+ [isIn, width, opacity],
35
35
  )
36
36
 
37
+ useLayoutEffect(() => {
38
+ if (contentRef.current) {
39
+ contentRef.current.measure((_x, _y, width) => {
40
+ setContentWidth(width)
41
+ })
42
+ }
43
+ }, [])
44
+
37
45
  React.useEffect(() => {
38
46
  if (isIn) {
39
47
  // Fade in: width first, then opacity
40
- width.value = withTiming(contentWidth.value, {
48
+ width.value = withTiming(contentWidth, {
41
49
  duration,
42
50
  })
43
51
  opacity.value = withSequence(
@@ -58,7 +66,7 @@ export const CollapseHorizontal = ({
58
66
  }),
59
67
  )
60
68
  }
61
- }, [isIn, contentWidth.value, duration, width, opacity])
69
+ }, [isIn, contentWidth, duration, width, opacity])
62
70
 
63
71
  const animatedStyle = useAnimatedStyle(() => ({
64
72
  width: width.value,
@@ -74,8 +82,10 @@ export const CollapseHorizontal = ({
74
82
  },
75
83
  animatedStyle,
76
84
  ]}>
77
- <View style={[{ position: 'absolute' }, style]} onLayout={handleLayout}>
78
- {children}
85
+ <View style={{ position: 'absolute', width: 'auto' }}>
86
+ <View style={style} onLayout={handleLayout} ref={contentRef}>
87
+ {children}
88
+ </View>
79
89
  </View>
80
90
  </Animated.View>
81
91
  )
@@ -1,14 +1,15 @@
1
1
  import { useMounted } from '@chem-po/react'
2
2
  import { Ionicons } from '@expo/vector-icons'
3
3
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
4
- import { DimensionValue, Image, StyleSheet, TouchableOpacity, View, ViewStyle } from 'react-native'
4
+ import { DimensionValue, Image, StyleSheet, View, ViewStyle } from 'react-native'
5
+ import { Pressable } from 'react-native-gesture-handler'
5
6
  import { ImageViewModal } from '../image/ImageViewModal'
6
7
  import { LoadingLogo } from './Loading'
7
8
 
8
9
  export interface LoadingImageProps {
9
10
  src?: string | null
10
11
  loadingOverride?: boolean
11
- onLoad?: (e: any) => void
12
+ onLoad?: (dims: { width: number; height: number }) => void
12
13
  filename?: string
13
14
  fileType?: string
14
15
  withFullView?: boolean
@@ -41,12 +42,13 @@ export const LoadingImage = ({
41
42
  const [viewing, setViewing] = useState(false)
42
43
  const mounted = useMounted(100)
43
44
 
44
- const handleImageLoad = useCallback(
45
- (e: any) => {
46
- if (onLoad) onLoad(e)
47
- },
48
- [onLoad],
49
- )
45
+ const handleImageLoad = useCallback(() => {
46
+ if (onLoad && src) {
47
+ Image.getSize(src, (width, height) => {
48
+ onLoad({ width, height })
49
+ })
50
+ }
51
+ }, [onLoad, src])
50
52
 
51
53
  const [prevSrc, setPrevSrc] = useState(src)
52
54
  useEffect(() => {
@@ -65,7 +67,6 @@ export const LoadingImage = ({
65
67
  opacity: src && !loading ? 1 : 0,
66
68
  },
67
69
  ]}
68
- // onLoadStart={() => setImageLoading(true)}
69
70
  onLoadEnd={() => setImageLoading(false)}
70
71
  onLoad={handleImageLoad}
71
72
  resizeMode={src ? 'cover' : 'contain'}
@@ -86,23 +87,22 @@ export const LoadingImage = ({
86
87
  {!withFullView || buttonFullView ? (
87
88
  ImageComponent
88
89
  ) : (
89
- <TouchableOpacity
90
- activeOpacity={0.9}
90
+ <Pressable
91
91
  onPress={() => setViewing(true)}
92
92
  style={{
93
- width: width ? undefined : '100%',
94
- height: height ? undefined : '100%',
93
+ position: 'absolute',
94
+ top: 0,
95
+ left: 0,
96
+ right: 0,
97
+ bottom: 0,
95
98
  }}>
96
99
  {ImageComponent}
97
- </TouchableOpacity>
100
+ </Pressable>
98
101
  )}
99
102
  {buttonFullView && (
100
- <TouchableOpacity
101
- style={styles.fullViewButton}
102
- onPress={() => setViewing(true)}
103
- activeOpacity={0.7}>
103
+ <Pressable style={styles.fullViewButton} onPress={() => setViewing(true)}>
104
104
  <Ionicons name="expand" size={20} color="white" style={styles.fullViewIcon} />
105
- </TouchableOpacity>
105
+ </Pressable>
106
106
  )}
107
107
  <View
108
108
  style={[
@@ -1,7 +1,8 @@
1
1
  import { useBackgroundColor, useIconColor, useTheme } from '@chem-po/react'
2
2
  import { Ionicons } from '@expo/vector-icons'
3
3
  import React from 'react'
4
- import { StyleSheet, TouchableOpacity, View } from 'react-native'
4
+ import { StyleSheet, View } from 'react-native'
5
+ import { Pressable } from 'react-native-gesture-handler'
5
6
 
6
7
  export const DarkModeToggle = () => {
7
8
  const { colorMode, toggleColorMode } = useTheme()
@@ -11,7 +12,7 @@ export const DarkModeToggle = () => {
11
12
  const iconName = isDark ? 'moon' : 'sunny'
12
13
  const bg = useBackgroundColor(100)
13
14
  return (
14
- <TouchableOpacity
15
+ <Pressable
15
16
  onPress={toggleColorMode}
16
17
  style={[
17
18
  styles.button,
@@ -20,7 +21,7 @@ export const DarkModeToggle = () => {
20
21
  },
21
22
  ]}>
22
23
  <Ionicons name={iconName} size={16} color={iconColor} />
23
- </TouchableOpacity>
24
+ </Pressable>
24
25
  )
25
26
  }
26
27
 
@@ -1,36 +1,46 @@
1
1
  import * as FileSystem from 'expo-file-system'
2
+ import { downloadAsync } from 'expo-file-system'
3
+ import { shareAsync } from 'expo-sharing'
4
+ import { Platform } from 'react-native'
2
5
 
3
6
  export const downloadFile = async (uri: string, filename: string, fileType: string) => {
4
- const permissions = await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync()
5
- if (!permissions.granted) {
6
- return
7
+ if (Platform.OS === 'android') {
8
+ const permissions = await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync()
9
+ if (!permissions.granted) {
10
+ throw new Error('Permission not granted')
11
+ }
12
+
13
+ // Download to temporary location first
14
+ const tempFileUri = `${FileSystem.cacheDirectory}${filename}`
15
+
16
+ // Download the file to temporary location
17
+ await FileSystem.downloadAsync(uri, tempFileUri)
18
+
19
+ // Read the temporary file as base64
20
+ const base64Content = await FileSystem.readAsStringAsync(tempFileUri, {
21
+ encoding: FileSystem.EncodingType.Base64,
22
+ })
23
+
24
+ // Create a file within the selected directory using SAF
25
+ const fileUri = await FileSystem.StorageAccessFramework.createFileAsync(
26
+ permissions.directoryUri,
27
+ filename,
28
+ fileType,
29
+ )
30
+
31
+ // Write the content to the SAF file
32
+ await FileSystem.StorageAccessFramework.writeAsStringAsync(fileUri, base64Content, {
33
+ encoding: FileSystem.EncodingType.Base64,
34
+ })
35
+
36
+ // Clean up temporary file
37
+ await FileSystem.deleteAsync(tempFileUri, { idempotent: true })
38
+
39
+ return { uri: fileUri }
7
40
  }
8
-
9
- // Download to temporary location first
10
- const tempFileUri = `${FileSystem.cacheDirectory}${filename}`
11
-
12
- // Download the file to temporary location
13
- await FileSystem.downloadAsync(uri, tempFileUri)
14
-
15
- // Read the temporary file as base64
16
- const base64Content = await FileSystem.readAsStringAsync(tempFileUri, {
17
- encoding: FileSystem.EncodingType.Base64,
18
- })
19
-
20
- // Create a file within the selected directory using SAF
21
- const fileUri = await FileSystem.StorageAccessFramework.createFileAsync(
22
- permissions.directoryUri,
23
- filename,
24
- fileType,
25
- )
26
-
27
- // Write the content to the SAF file
28
- await FileSystem.StorageAccessFramework.writeAsStringAsync(fileUri, base64Content, {
29
- encoding: FileSystem.EncodingType.Base64,
30
- })
31
-
32
- // Clean up temporary file
33
- await FileSystem.deleteAsync(tempFileUri, { idempotent: true })
34
-
35
- return { uri: fileUri }
41
+ if (!FileSystem.documentDirectory) {
42
+ throw new Error('Document directory not found')
43
+ }
44
+ const result = await downloadAsync(uri, FileSystem.documentDirectory + filename)
45
+ return shareAsync(result.uri)
36
46
  }