@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.
- package/lib/commonjs/components/button/DeleteButton.js +7 -8
- package/lib/commonjs/components/button/DeleteButton.js.map +1 -1
- package/lib/commonjs/components/button/LoadingButton.js +3 -3
- package/lib/commonjs/components/button/LoadingButton.js.map +1 -1
- package/lib/commonjs/components/button/Toggle.js +2 -1
- package/lib/commonjs/components/button/Toggle.js.map +1 -1
- package/lib/commonjs/components/form/Form.js +30 -4
- package/lib/commonjs/components/form/Form.js.map +1 -1
- package/lib/commonjs/components/form/FormFooter.js +3 -2
- package/lib/commonjs/components/form/FormFooter.js.map +1 -1
- package/lib/commonjs/components/form/input/Editable.js +4 -6
- package/lib/commonjs/components/form/input/Editable.js.map +1 -1
- package/lib/commonjs/components/form/input/common/InputClearButton.js +2 -1
- package/lib/commonjs/components/form/input/common/InputClearButton.js.map +1 -1
- package/lib/commonjs/components/form/input/date/index.js +23 -10
- package/lib/commonjs/components/form/input/date/index.js.map +1 -1
- package/lib/commonjs/components/form/input/datetime/index.js +27 -11
- package/lib/commonjs/components/form/input/datetime/index.js.map +1 -1
- package/lib/commonjs/components/form/input/file/index.js +31 -28
- package/lib/commonjs/components/form/input/file/index.js.map +1 -1
- package/lib/commonjs/components/form/input/hooks/useInputStyles.js +5 -6
- package/lib/commonjs/components/form/input/hooks/useInputStyles.js.map +1 -1
- package/lib/commonjs/components/form/input/input.js +3 -3
- package/lib/commonjs/components/form/input/input.js.map +1 -1
- package/lib/commonjs/components/form/input/multipleSelect/index.js +35 -23
- package/lib/commonjs/components/form/input/multipleSelect/index.js.map +1 -1
- package/lib/commonjs/components/form/input/socialMedia/index.js +3 -2
- package/lib/commonjs/components/form/input/socialMedia/index.js.map +1 -1
- package/lib/commonjs/components/form/input/text/AutoResizeTextarea.js +1 -6
- package/lib/commonjs/components/form/input/text/AutoResizeTextarea.js.map +1 -1
- package/lib/commonjs/components/form/input/text/index.js +2 -1
- package/lib/commonjs/components/form/input/text/index.js.map +1 -1
- package/lib/commonjs/components/form/input/text/textarea.js +2 -1
- package/lib/commonjs/components/form/input/text/textarea.js.map +1 -1
- package/lib/commonjs/components/form/input/text/useWebAutoResize.js +4 -4
- package/lib/commonjs/components/form/input/text/useWebAutoResize.js.map +1 -1
- package/lib/commonjs/components/form/input/time/index.js +2 -1
- package/lib/commonjs/components/form/input/time/index.js.map +1 -1
- package/lib/commonjs/components/form/view/select.js +6 -2
- package/lib/commonjs/components/form/view/select.js.map +1 -1
- package/lib/commonjs/components/image/ImageViewModal.js +22 -16
- package/lib/commonjs/components/image/ImageViewModal.js.map +1 -1
- package/lib/commonjs/components/layout/CollapseHorizontal.js +23 -11
- package/lib/commonjs/components/layout/CollapseHorizontal.js.map +1 -1
- package/lib/commonjs/components/loading/LoadingImage.js +20 -13
- package/lib/commonjs/components/loading/LoadingImage.js.map +1 -1
- package/lib/commonjs/components/theme/colorMode/DarkModeToggle.js +2 -1
- package/lib/commonjs/components/theme/colorMode/DarkModeToggle.js.map +1 -1
- package/lib/commonjs/utils/downloadFile.js +36 -26
- package/lib/commonjs/utils/downloadFile.js.map +1 -1
- package/lib/module/components/button/DeleteButton.js +8 -9
- package/lib/module/components/button/DeleteButton.js.map +1 -1
- package/lib/module/components/button/LoadingButton.js +4 -4
- package/lib/module/components/button/LoadingButton.js.map +1 -1
- package/lib/module/components/button/Toggle.js +3 -2
- package/lib/module/components/button/Toggle.js.map +1 -1
- package/lib/module/components/form/Form.js +31 -5
- package/lib/module/components/form/Form.js.map +1 -1
- package/lib/module/components/form/FormFooter.js +4 -3
- package/lib/module/components/form/FormFooter.js.map +1 -1
- package/lib/module/components/form/input/Editable.js +5 -7
- package/lib/module/components/form/input/Editable.js.map +1 -1
- package/lib/module/components/form/input/common/InputClearButton.js +3 -2
- package/lib/module/components/form/input/common/InputClearButton.js.map +1 -1
- package/lib/module/components/form/input/date/index.js +24 -11
- package/lib/module/components/form/input/date/index.js.map +1 -1
- package/lib/module/components/form/input/datetime/index.js +29 -13
- package/lib/module/components/form/input/datetime/index.js.map +1 -1
- package/lib/module/components/form/input/file/index.js +33 -30
- package/lib/module/components/form/input/file/index.js.map +1 -1
- package/lib/module/components/form/input/hooks/useInputStyles.js +5 -6
- package/lib/module/components/form/input/hooks/useInputStyles.js.map +1 -1
- package/lib/module/components/form/input/input.js +3 -3
- package/lib/module/components/form/input/input.js.map +1 -1
- package/lib/module/components/form/input/multipleSelect/index.js +35 -23
- package/lib/module/components/form/input/multipleSelect/index.js.map +1 -1
- package/lib/module/components/form/input/socialMedia/index.js +4 -3
- package/lib/module/components/form/input/socialMedia/index.js.map +1 -1
- package/lib/module/components/form/input/text/AutoResizeTextarea.js +1 -6
- package/lib/module/components/form/input/text/AutoResizeTextarea.js.map +1 -1
- package/lib/module/components/form/input/text/index.js +3 -2
- package/lib/module/components/form/input/text/index.js.map +1 -1
- package/lib/module/components/form/input/text/textarea.js +2 -1
- package/lib/module/components/form/input/text/textarea.js.map +1 -1
- package/lib/module/components/form/input/text/useWebAutoResize.js +4 -4
- package/lib/module/components/form/input/text/useWebAutoResize.js.map +1 -1
- package/lib/module/components/form/input/time/index.js +3 -2
- package/lib/module/components/form/input/time/index.js.map +1 -1
- package/lib/module/components/form/view/select.js +6 -2
- package/lib/module/components/form/view/select.js.map +1 -1
- package/lib/module/components/image/ImageViewModal.js +24 -18
- package/lib/module/components/image/ImageViewModal.js.map +1 -1
- package/lib/module/components/layout/CollapseHorizontal.js +24 -12
- package/lib/module/components/layout/CollapseHorizontal.js.map +1 -1
- package/lib/module/components/loading/LoadingImage.js +21 -14
- package/lib/module/components/loading/LoadingImage.js.map +1 -1
- package/lib/module/components/theme/colorMode/DarkModeToggle.js +3 -2
- package/lib/module/components/theme/colorMode/DarkModeToggle.js.map +1 -1
- package/lib/module/utils/downloadFile.js +35 -25
- package/lib/module/utils/downloadFile.js.map +1 -1
- package/lib/typescript/components/button/DeleteButton.d.ts.map +1 -1
- package/lib/typescript/components/button/LoadingButton.d.ts.map +1 -1
- package/lib/typescript/components/button/Toggle.d.ts.map +1 -1
- package/lib/typescript/components/form/Form.d.ts.map +1 -1
- package/lib/typescript/components/form/FormFooter.d.ts.map +1 -1
- package/lib/typescript/components/form/input/Editable.d.ts.map +1 -1
- package/lib/typescript/components/form/input/common/InputClearButton.d.ts.map +1 -1
- package/lib/typescript/components/form/input/date/index.d.ts.map +1 -1
- package/lib/typescript/components/form/input/datetime/index.d.ts.map +1 -1
- package/lib/typescript/components/form/input/file/index.d.ts +2 -1
- package/lib/typescript/components/form/input/file/index.d.ts.map +1 -1
- package/lib/typescript/components/form/input/hooks/useInputStyles.d.ts +3 -6
- package/lib/typescript/components/form/input/hooks/useInputStyles.d.ts.map +1 -1
- package/lib/typescript/components/form/input/multipleSelect/index.d.ts.map +1 -1
- package/lib/typescript/components/form/input/socialMedia/index.d.ts.map +1 -1
- package/lib/typescript/components/form/input/text/AutoResizeTextarea.d.ts.map +1 -1
- package/lib/typescript/components/form/input/text/index.d.ts.map +1 -1
- package/lib/typescript/components/form/input/text/useWebAutoResize.d.ts +2 -3
- package/lib/typescript/components/form/input/text/useWebAutoResize.d.ts.map +1 -1
- package/lib/typescript/components/form/input/time/index.d.ts.map +1 -1
- package/lib/typescript/components/form/view/select.d.ts.map +1 -1
- package/lib/typescript/components/image/ImageViewModal.d.ts.map +1 -1
- package/lib/typescript/components/layout/CollapseHorizontal.d.ts.map +1 -1
- package/lib/typescript/components/loading/LoadingImage.d.ts +4 -1
- package/lib/typescript/components/loading/LoadingImage.d.ts.map +1 -1
- package/lib/typescript/components/theme/colorMode/DarkModeToggle.d.ts.map +1 -1
- package/lib/typescript/utils/downloadFile.d.ts +2 -2
- package/lib/typescript/utils/downloadFile.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/components/button/DeleteButton.tsx +12 -25
- package/src/components/button/LoadingButton.tsx +4 -5
- package/src/components/button/Toggle.tsx +4 -3
- package/src/components/form/Form.tsx +31 -11
- package/src/components/form/FormFooter.tsx +6 -5
- package/src/components/form/input/Editable.tsx +8 -9
- package/src/components/form/input/common/InputClearButton.tsx +4 -3
- package/src/components/form/input/date/index.tsx +24 -11
- package/src/components/form/input/datetime/index.tsx +33 -16
- package/src/components/form/input/file/index.tsx +38 -36
- package/src/components/form/input/hooks/useInputStyles.ts +10 -7
- package/src/components/form/input/input.tsx +2 -2
- package/src/components/form/input/multipleSelect/index.tsx +35 -35
- package/src/components/form/input/socialMedia/index.tsx +6 -5
- package/src/components/form/input/text/AutoResizeTextarea.tsx +1 -5
- package/src/components/form/input/text/index.tsx +4 -3
- package/src/components/form/input/text/textarea.tsx +1 -1
- package/src/components/form/input/text/useWebAutoResize.tsx +6 -4
- package/src/components/form/input/time/index.tsx +4 -3
- package/src/components/form/view/select.tsx +5 -1
- package/src/components/image/ImageViewModal.tsx +60 -51
- package/src/components/layout/CollapseHorizontal.tsx +20 -10
- package/src/components/loading/LoadingImage.tsx +19 -19
- package/src/components/theme/colorMode/DarkModeToggle.tsx +4 -3
- 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,
|
|
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
|
-
<
|
|
67
|
+
<Pressable style={styles.eyeIcon} onPress={() => setIsHidden(!isHidden)}>
|
|
67
68
|
<Ionicons name={isHidden ? 'eye' : 'eye-off'} size={20} color="#666" />
|
|
68
|
-
</
|
|
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
|
|
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 ??
|
|
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,
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
|
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,
|
|
5
|
-
import {
|
|
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((
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
207
|
-
<
|
|
208
|
-
<
|
|
209
|
-
style={styles.backgroundTouchable}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
248
|
+
<Pressable style={styles.closeButton} onPress={onClose}>
|
|
249
|
+
<Ionicons name="close" size={24} color="white" />
|
|
250
|
+
</Pressable>
|
|
244
251
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
252
|
+
{loading || !src ? (
|
|
253
|
+
<View style={styles.loadingContainer}>
|
|
254
|
+
<LoadingLogo isLoading={loading} size={70} />
|
|
255
|
+
</View>
|
|
256
|
+
) : null}
|
|
257
|
+
</View>
|
|
250
258
|
</View>
|
|
251
|
-
</
|
|
252
|
-
</
|
|
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
|
|
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
|
-
|
|
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 =
|
|
30
|
+
width.value = event.nativeEvent.layout.width
|
|
31
31
|
opacity.value = 1
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
|
-
[isIn,
|
|
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
|
|
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
|
|
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={
|
|
78
|
-
{
|
|
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,
|
|
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?: (
|
|
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
|
-
(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
<
|
|
90
|
-
activeOpacity={0.9}
|
|
90
|
+
<Pressable
|
|
91
91
|
onPress={() => setViewing(true)}
|
|
92
92
|
style={{
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
position: 'absolute',
|
|
94
|
+
top: 0,
|
|
95
|
+
left: 0,
|
|
96
|
+
right: 0,
|
|
97
|
+
bottom: 0,
|
|
95
98
|
}}>
|
|
96
99
|
{ImageComponent}
|
|
97
|
-
</
|
|
100
|
+
</Pressable>
|
|
98
101
|
)}
|
|
99
102
|
{buttonFullView && (
|
|
100
|
-
<
|
|
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
|
-
</
|
|
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,
|
|
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
|
-
<
|
|
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
|
-
</
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
}
|