@one-am/react-native-simple-image-slider 0.2.2
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/LICENSE +20 -0
- package/README.md +74 -0
- package/lib/commonjs/@types/styled.d.js +4 -0
- package/lib/commonjs/@types/styled.d.js.map +1 -0
- package/lib/commonjs/BaseSimpleImageSlider.js +194 -0
- package/lib/commonjs/BaseSimpleImageSlider.js.map +1 -0
- package/lib/commonjs/FullScreenImageSlider.js +126 -0
- package/lib/commonjs/FullScreenImageSlider.js.map +1 -0
- package/lib/commonjs/PageCounter.js +39 -0
- package/lib/commonjs/PageCounter.js.map +1 -0
- package/lib/commonjs/PinchToZoom.js +173 -0
- package/lib/commonjs/PinchToZoom.js.map +1 -0
- package/lib/commonjs/SimpleImageSlider.js +59 -0
- package/lib/commonjs/SimpleImageSlider.js.map +1 -0
- package/lib/commonjs/StyledComponentsThemeProvider.js +51 -0
- package/lib/commonjs/StyledComponentsThemeProvider.js.map +1 -0
- package/lib/commonjs/index.js +35 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/utils/clamp.js +13 -0
- package/lib/commonjs/utils/clamp.js.map +1 -0
- package/lib/commonjs/utils/renderProp.js +17 -0
- package/lib/commonjs/utils/renderProp.js.map +1 -0
- package/lib/module/@types/styled.d.js +2 -0
- package/lib/module/@types/styled.d.js.map +1 -0
- package/lib/module/BaseSimpleImageSlider.js +185 -0
- package/lib/module/BaseSimpleImageSlider.js.map +1 -0
- package/lib/module/FullScreenImageSlider.js +117 -0
- package/lib/module/FullScreenImageSlider.js.map +1 -0
- package/lib/module/PageCounter.js +31 -0
- package/lib/module/PageCounter.js.map +1 -0
- package/lib/module/PinchToZoom.js +165 -0
- package/lib/module/PinchToZoom.js.map +1 -0
- package/lib/module/SimpleImageSlider.js +50 -0
- package/lib/module/SimpleImageSlider.js.map +1 -0
- package/lib/module/StyledComponentsThemeProvider.js +43 -0
- package/lib/module/StyledComponentsThemeProvider.js.map +1 -0
- package/lib/module/index.js +6 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/utils/clamp.js +6 -0
- package/lib/module/utils/clamp.js.map +1 -0
- package/lib/module/utils/renderProp.js +9 -0
- package/lib/module/utils/renderProp.js.map +1 -0
- package/lib/typescript/src/BaseSimpleImageSlider.d.ts +33 -0
- package/lib/typescript/src/BaseSimpleImageSlider.d.ts.map +1 -0
- package/lib/typescript/src/FullScreenImageSlider.d.ts +15 -0
- package/lib/typescript/src/FullScreenImageSlider.d.ts.map +1 -0
- package/lib/typescript/src/PageCounter.d.ts +10 -0
- package/lib/typescript/src/PageCounter.d.ts.map +1 -0
- package/lib/typescript/src/PinchToZoom.d.ts +18 -0
- package/lib/typescript/src/PinchToZoom.d.ts.map +1 -0
- package/lib/typescript/src/SimpleImageSlider.d.ts +11 -0
- package/lib/typescript/src/SimpleImageSlider.d.ts.map +1 -0
- package/lib/typescript/src/StyledComponentsThemeProvider.d.ts +3 -0
- package/lib/typescript/src/StyledComponentsThemeProvider.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +6 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/utils/clamp.d.ts +2 -0
- package/lib/typescript/src/utils/clamp.d.ts.map +1 -0
- package/lib/typescript/src/utils/renderProp.d.ts +4 -0
- package/lib/typescript/src/utils/renderProp.d.ts.map +1 -0
- package/package.json +170 -0
- package/src/@types/styled.d.ts +39 -0
- package/src/BaseSimpleImageSlider.tsx +281 -0
- package/src/FullScreenImageSlider.tsx +164 -0
- package/src/PageCounter.tsx +39 -0
- package/src/PinchToZoom.tsx +323 -0
- package/src/SimpleImageSlider.tsx +72 -0
- package/src/StyledComponentsThemeProvider.tsx +48 -0
- package/src/index.tsx +14 -0
- package/src/utils/clamp.ts +4 -0
- package/src/utils/renderProp.tsx +22 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { type StyleProp, StyleSheet, Text, View, type ViewStyle } from 'react-native';
|
|
3
|
+
import { type DefaultTheme, useTheme } from 'styled-components/native';
|
|
4
|
+
|
|
5
|
+
type PageCounterProps = {
|
|
6
|
+
currentPage: number;
|
|
7
|
+
totalPages: number;
|
|
8
|
+
style?: StyleProp<ViewStyle>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function PageCounter({ currentPage, totalPages, style }: PageCounterProps) {
|
|
12
|
+
const theme = useTheme();
|
|
13
|
+
const styles = useMemo(() => makeStyles(theme), [theme]);
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<View style={[styles.container, style]}>
|
|
17
|
+
<Text>
|
|
18
|
+
{currentPage} / {totalPages}
|
|
19
|
+
</Text>
|
|
20
|
+
</View>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const makeStyles = (theme: DefaultTheme) => {
|
|
25
|
+
return StyleSheet.create({
|
|
26
|
+
container: {
|
|
27
|
+
backgroundColor: theme.colors.pageCounterBackground,
|
|
28
|
+
borderWidth: 1,
|
|
29
|
+
borderColor: theme.colors.pageCounterBorder,
|
|
30
|
+
borderRadius: 8,
|
|
31
|
+
paddingHorizontal: 5,
|
|
32
|
+
paddingVertical: 6,
|
|
33
|
+
width: 75,
|
|
34
|
+
flexDirection: 'row',
|
|
35
|
+
alignItems: 'center',
|
|
36
|
+
justifyContent: 'center',
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
};
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import React, { type PropsWithChildren, useCallback, useMemo } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
type LayoutChangeEvent,
|
|
4
|
+
type StyleProp,
|
|
5
|
+
useWindowDimensions,
|
|
6
|
+
type ViewStyle,
|
|
7
|
+
} from 'react-native';
|
|
8
|
+
import Animated, {
|
|
9
|
+
cancelAnimation,
|
|
10
|
+
runOnJS,
|
|
11
|
+
type SharedValue,
|
|
12
|
+
useAnimatedReaction,
|
|
13
|
+
useAnimatedStyle,
|
|
14
|
+
useSharedValue,
|
|
15
|
+
withTiming,
|
|
16
|
+
} from 'react-native-reanimated';
|
|
17
|
+
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
18
|
+
import { clamp } from './utils/clamp';
|
|
19
|
+
import * as Haptics from 'expo-haptics';
|
|
20
|
+
|
|
21
|
+
export type PinchToZoomProps = PropsWithChildren<{
|
|
22
|
+
minimumZoomScale?: number;
|
|
23
|
+
maximumZoomScale?: number;
|
|
24
|
+
style?: StyleProp<ViewStyle>;
|
|
25
|
+
onPinchStart?: () => void;
|
|
26
|
+
onPinchEnd?: () => void;
|
|
27
|
+
disabled?: boolean;
|
|
28
|
+
onLayout?: (e: LayoutChangeEvent) => void;
|
|
29
|
+
onScaleChange?: () => void;
|
|
30
|
+
onScaleReset?: () => void;
|
|
31
|
+
onTranslationChange?: (
|
|
32
|
+
x: SharedValue<number>,
|
|
33
|
+
y: SharedValue<number>,
|
|
34
|
+
scale: SharedValue<number>
|
|
35
|
+
) => void;
|
|
36
|
+
onRequestClose?: () => void;
|
|
37
|
+
}>;
|
|
38
|
+
|
|
39
|
+
export default function PinchToZoom({
|
|
40
|
+
minimumZoomScale = 1,
|
|
41
|
+
maximumZoomScale = 8,
|
|
42
|
+
style: propStyle,
|
|
43
|
+
onPinchStart,
|
|
44
|
+
onPinchEnd,
|
|
45
|
+
disabled,
|
|
46
|
+
onLayout,
|
|
47
|
+
onTranslationChange,
|
|
48
|
+
onScaleChange,
|
|
49
|
+
onScaleReset,
|
|
50
|
+
children,
|
|
51
|
+
onRequestClose,
|
|
52
|
+
}: PinchToZoomProps) {
|
|
53
|
+
const { height: windowHeight } = useWindowDimensions();
|
|
54
|
+
|
|
55
|
+
const translationX = useSharedValue(0);
|
|
56
|
+
const translationY = useSharedValue(0);
|
|
57
|
+
const originX = useSharedValue(0);
|
|
58
|
+
const originY = useSharedValue(0);
|
|
59
|
+
const scale = useSharedValue(1);
|
|
60
|
+
const isPinching = useSharedValue(false);
|
|
61
|
+
const viewHeight = useSharedValue(0);
|
|
62
|
+
const viewWidth = useSharedValue(0);
|
|
63
|
+
|
|
64
|
+
const prevScale = useSharedValue(0);
|
|
65
|
+
const offsetScale = useSharedValue(0);
|
|
66
|
+
const prevTranslationX = useSharedValue(0);
|
|
67
|
+
const prevTranslationY = useSharedValue(0);
|
|
68
|
+
|
|
69
|
+
const pinchGesture = useMemo(
|
|
70
|
+
() =>
|
|
71
|
+
Gesture.Pinch()
|
|
72
|
+
.enabled(!disabled)
|
|
73
|
+
.onStart(() => {
|
|
74
|
+
cancelAnimation(translationX);
|
|
75
|
+
cancelAnimation(translationY);
|
|
76
|
+
cancelAnimation(scale);
|
|
77
|
+
prevScale.value = scale.value;
|
|
78
|
+
offsetScale.value = scale.value;
|
|
79
|
+
if (onPinchStart) runOnJS(onPinchStart)();
|
|
80
|
+
if (onScaleChange) runOnJS(onScaleChange)();
|
|
81
|
+
})
|
|
82
|
+
.onUpdate((e) => {
|
|
83
|
+
if (e.numberOfPointers === 2) {
|
|
84
|
+
scale.value = Math.min(prevScale.value * e.scale, maximumZoomScale);
|
|
85
|
+
|
|
86
|
+
// reset the origin
|
|
87
|
+
if (!isPinching.value) {
|
|
88
|
+
isPinching.value = true;
|
|
89
|
+
originX.value = e.focalX;
|
|
90
|
+
originY.value = e.focalY;
|
|
91
|
+
prevTranslationX.value = translationX.value;
|
|
92
|
+
prevTranslationY.value = translationY.value;
|
|
93
|
+
offsetScale.value = scale.value;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (isPinching.value) {
|
|
97
|
+
// translate the image to the focal point as we're zooming
|
|
98
|
+
translationX.value = clamp(
|
|
99
|
+
prevTranslationX.value +
|
|
100
|
+
-1 *
|
|
101
|
+
((scale.value - offsetScale.value) *
|
|
102
|
+
(originX.value - viewWidth.value / 2)),
|
|
103
|
+
(-viewWidth.value * (scale.value - minimumZoomScale)) / 2,
|
|
104
|
+
(viewWidth.value * (scale.value - minimumZoomScale)) / 2
|
|
105
|
+
);
|
|
106
|
+
translationY.value = clamp(
|
|
107
|
+
prevTranslationY.value +
|
|
108
|
+
-1 *
|
|
109
|
+
((scale.value - offsetScale.value) *
|
|
110
|
+
(originY.value - viewHeight.value / 2)),
|
|
111
|
+
(-viewHeight.value * (scale.value - minimumZoomScale)) / 2,
|
|
112
|
+
(viewHeight.value * (scale.value - minimumZoomScale)) / 2
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
.onEnd(() => {
|
|
118
|
+
isPinching.value = false;
|
|
119
|
+
|
|
120
|
+
if (scale.value < minimumZoomScale / 2 && prevScale.value <= minimumZoomScale) {
|
|
121
|
+
if (onRequestClose) {
|
|
122
|
+
runOnJS(onRequestClose)();
|
|
123
|
+
}
|
|
124
|
+
} else if (scale.value < minimumZoomScale) {
|
|
125
|
+
runOnJS(Haptics.impactAsync)(Haptics.ImpactFeedbackStyle.Light);
|
|
126
|
+
translationX.value = withTiming(0);
|
|
127
|
+
translationY.value = withTiming(0);
|
|
128
|
+
scale.value = withTiming(minimumZoomScale);
|
|
129
|
+
if (onScaleReset) {
|
|
130
|
+
runOnJS(onScaleReset)();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
prevScale.value = 0;
|
|
135
|
+
prevTranslationX.value = translationX.value;
|
|
136
|
+
prevTranslationY.value = translationY.value;
|
|
137
|
+
|
|
138
|
+
if (onPinchEnd) runOnJS(onPinchEnd)();
|
|
139
|
+
}),
|
|
140
|
+
|
|
141
|
+
[
|
|
142
|
+
disabled,
|
|
143
|
+
translationX,
|
|
144
|
+
translationY,
|
|
145
|
+
scale,
|
|
146
|
+
prevScale,
|
|
147
|
+
offsetScale,
|
|
148
|
+
onPinchStart,
|
|
149
|
+
onScaleChange,
|
|
150
|
+
maximumZoomScale,
|
|
151
|
+
isPinching,
|
|
152
|
+
originX,
|
|
153
|
+
originY,
|
|
154
|
+
prevTranslationX,
|
|
155
|
+
prevTranslationY,
|
|
156
|
+
viewWidth.value,
|
|
157
|
+
viewHeight.value,
|
|
158
|
+
minimumZoomScale,
|
|
159
|
+
onPinchEnd,
|
|
160
|
+
onRequestClose,
|
|
161
|
+
onScaleReset,
|
|
162
|
+
]
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const panGesture = useMemo(
|
|
166
|
+
() =>
|
|
167
|
+
Gesture.Pan()
|
|
168
|
+
.enabled(!disabled)
|
|
169
|
+
.onStart(() => {
|
|
170
|
+
cancelAnimation(translationX);
|
|
171
|
+
cancelAnimation(translationY);
|
|
172
|
+
|
|
173
|
+
prevTranslationX.value = translationX.value;
|
|
174
|
+
prevTranslationY.value = translationY.value;
|
|
175
|
+
})
|
|
176
|
+
.onUpdate((e) => {
|
|
177
|
+
if (prevScale.value <= minimumZoomScale) {
|
|
178
|
+
translationX.value = prevTranslationX.value + e.translationX;
|
|
179
|
+
translationY.value = prevTranslationY.value + e.translationY;
|
|
180
|
+
} else {
|
|
181
|
+
translationX.value = clamp(
|
|
182
|
+
prevTranslationX.value + e.translationX,
|
|
183
|
+
(-viewWidth.value * (scale.value - minimumZoomScale)) / 2,
|
|
184
|
+
(viewWidth.value * (scale.value - minimumZoomScale)) / 2
|
|
185
|
+
);
|
|
186
|
+
translationY.value = clamp(
|
|
187
|
+
prevTranslationY.value + e.translationY,
|
|
188
|
+
(-viewHeight.value * (scale.value - minimumZoomScale)) / 2,
|
|
189
|
+
(viewHeight.value * (scale.value - minimumZoomScale)) / 2
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
.onEnd(() => {
|
|
194
|
+
if (scale.value <= minimumZoomScale && prevScale.value <= minimumZoomScale) {
|
|
195
|
+
if (
|
|
196
|
+
Math.abs(translationX.value) > viewWidth.value / 2 ||
|
|
197
|
+
Math.abs(translationY.value) > viewHeight.value / 2
|
|
198
|
+
) {
|
|
199
|
+
if (onRequestClose) {
|
|
200
|
+
runOnJS(onRequestClose)();
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
runOnJS(Haptics.impactAsync)(Haptics.ImpactFeedbackStyle.Light);
|
|
204
|
+
translationX.value = withTiming(0);
|
|
205
|
+
translationY.value = withTiming(0);
|
|
206
|
+
}
|
|
207
|
+
} else if (
|
|
208
|
+
viewHeight.value * (scale.value - minimumZoomScale) <=
|
|
209
|
+
windowHeight
|
|
210
|
+
) {
|
|
211
|
+
translationX.value = withTiming(
|
|
212
|
+
clamp(
|
|
213
|
+
translationX.value,
|
|
214
|
+
(-viewWidth.value * (scale.value - minimumZoomScale)) / 2,
|
|
215
|
+
(viewWidth.value * (scale.value - minimumZoomScale)) / 2
|
|
216
|
+
)
|
|
217
|
+
);
|
|
218
|
+
translationY.value = withTiming(
|
|
219
|
+
clamp(
|
|
220
|
+
translationY.value,
|
|
221
|
+
(-viewHeight.value * (scale.value - minimumZoomScale)) / 2,
|
|
222
|
+
(viewHeight.value * (scale.value - minimumZoomScale)) / 2
|
|
223
|
+
)
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}),
|
|
227
|
+
[
|
|
228
|
+
disabled,
|
|
229
|
+
minimumZoomScale,
|
|
230
|
+
onRequestClose,
|
|
231
|
+
prevScale.value,
|
|
232
|
+
prevTranslationX,
|
|
233
|
+
prevTranslationY,
|
|
234
|
+
scale.value,
|
|
235
|
+
windowHeight,
|
|
236
|
+
translationX,
|
|
237
|
+
translationY,
|
|
238
|
+
viewHeight.value,
|
|
239
|
+
viewWidth.value,
|
|
240
|
+
]
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const tapGesture = useMemo(
|
|
244
|
+
() =>
|
|
245
|
+
Gesture.Tap()
|
|
246
|
+
.enabled(!disabled)
|
|
247
|
+
.numberOfTaps(2)
|
|
248
|
+
.onStart(() => {
|
|
249
|
+
if (scale.value > minimumZoomScale) {
|
|
250
|
+
translationX.value = withTiming(0);
|
|
251
|
+
translationY.value = withTiming(0);
|
|
252
|
+
scale.value = withTiming(minimumZoomScale);
|
|
253
|
+
if (onScaleReset) {
|
|
254
|
+
runOnJS(onScaleReset)();
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
scale.value = withTiming(maximumZoomScale / 2);
|
|
258
|
+
if (onScaleChange) {
|
|
259
|
+
runOnJS(onScaleChange)();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}),
|
|
263
|
+
[
|
|
264
|
+
disabled,
|
|
265
|
+
maximumZoomScale,
|
|
266
|
+
minimumZoomScale,
|
|
267
|
+
onScaleChange,
|
|
268
|
+
onScaleReset,
|
|
269
|
+
scale,
|
|
270
|
+
translationX,
|
|
271
|
+
translationY,
|
|
272
|
+
]
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
const compositeGesture = useMemo(() => {
|
|
276
|
+
return Gesture.Exclusive(Gesture.Simultaneous(pinchGesture, panGesture), tapGesture);
|
|
277
|
+
}, [panGesture, pinchGesture, tapGesture]);
|
|
278
|
+
|
|
279
|
+
useAnimatedReaction(
|
|
280
|
+
() => {
|
|
281
|
+
return {
|
|
282
|
+
scale: scale.value,
|
|
283
|
+
translationX: translationX.value,
|
|
284
|
+
translationY: translationY.value,
|
|
285
|
+
};
|
|
286
|
+
},
|
|
287
|
+
() => {
|
|
288
|
+
if (onTranslationChange) {
|
|
289
|
+
runOnJS(onTranslationChange)(translationX, translationY, scale);
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
[onTranslationChange]
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
const style = useAnimatedStyle(() => {
|
|
296
|
+
return {
|
|
297
|
+
transform: [
|
|
298
|
+
{ translateX: translationX.value },
|
|
299
|
+
{ translateY: translationY.value },
|
|
300
|
+
{ scale: scale.value },
|
|
301
|
+
],
|
|
302
|
+
};
|
|
303
|
+
}, []);
|
|
304
|
+
|
|
305
|
+
const internalOnLayout = useCallback(
|
|
306
|
+
(e: LayoutChangeEvent) => {
|
|
307
|
+
viewHeight.value = e.nativeEvent.layout.height;
|
|
308
|
+
viewWidth.value = e.nativeEvent.layout.width;
|
|
309
|
+
onLayout?.(e);
|
|
310
|
+
},
|
|
311
|
+
[viewHeight, viewWidth, onLayout]
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
const finalStyle = useMemo(() => [style, propStyle], [style, propStyle]);
|
|
315
|
+
|
|
316
|
+
return (
|
|
317
|
+
<GestureDetector gesture={compositeGesture}>
|
|
318
|
+
<Animated.View onLayout={internalOnLayout} style={finalStyle}>
|
|
319
|
+
{children}
|
|
320
|
+
</Animated.View>
|
|
321
|
+
</GestureDetector>
|
|
322
|
+
);
|
|
323
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { forwardRef, useCallback, useRef, useState } from 'react';
|
|
2
|
+
import { FlashList } from '@shopify/flash-list';
|
|
3
|
+
import mergeRefs from 'merge-refs';
|
|
4
|
+
import FullScreenImageSlider from './FullScreenImageSlider';
|
|
5
|
+
import BaseListImageSlider, {
|
|
6
|
+
type BaseSimpleImageSliderProps,
|
|
7
|
+
type SimpleImageSliderItem,
|
|
8
|
+
} from './BaseSimpleImageSlider';
|
|
9
|
+
|
|
10
|
+
export type SimpleImageSliderProps = BaseSimpleImageSliderProps & {
|
|
11
|
+
fullScreenEnabled?: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const SimpleImageSlider = forwardRef<FlashList<SimpleImageSliderItem>, SimpleImageSliderProps>(
|
|
15
|
+
function ListImageSlider(
|
|
16
|
+
{ data, fullScreenEnabled = false, onItemPress, onViewableItemChange, ...props },
|
|
17
|
+
ref
|
|
18
|
+
) {
|
|
19
|
+
const listRef = useRef<FlashList<SimpleImageSliderItem>>(null);
|
|
20
|
+
const fullScreenListRef = useRef<FlashList<SimpleImageSliderItem>>(null);
|
|
21
|
+
|
|
22
|
+
const [fullScreen, setFullScreen] = useState(false);
|
|
23
|
+
const [currentIndex, setCurrentIndex] = useState(0);
|
|
24
|
+
|
|
25
|
+
const internalOnViewableItemChange = useCallback(
|
|
26
|
+
(index: number) => {
|
|
27
|
+
setCurrentIndex(index);
|
|
28
|
+
onViewableItemChange?.(index);
|
|
29
|
+
},
|
|
30
|
+
[onViewableItemChange]
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const onFullScreenViewableItemChange = useCallback((index: number) => {
|
|
34
|
+
setCurrentIndex(index);
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
const openFullScreen = useCallback(() => {
|
|
38
|
+
setFullScreen(true);
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
const onRequestClose = useCallback(() => {
|
|
42
|
+
listRef.current?.scrollToIndex({ index: currentIndex });
|
|
43
|
+
setFullScreen(false);
|
|
44
|
+
}, [currentIndex]);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
<BaseListImageSlider
|
|
49
|
+
{...props}
|
|
50
|
+
data={data}
|
|
51
|
+
ref={mergeRefs(ref, listRef)}
|
|
52
|
+
onItemPress={onItemPress ?? openFullScreen}
|
|
53
|
+
onViewableItemChange={internalOnViewableItemChange}
|
|
54
|
+
/>
|
|
55
|
+
{fullScreenEnabled ? (
|
|
56
|
+
<FullScreenImageSlider
|
|
57
|
+
{...props}
|
|
58
|
+
ref={fullScreenListRef}
|
|
59
|
+
open={fullScreen}
|
|
60
|
+
onRequestClose={onRequestClose}
|
|
61
|
+
data={data}
|
|
62
|
+
showPageCounter={false}
|
|
63
|
+
indexOverride={currentIndex}
|
|
64
|
+
onViewableItemChange={onFullScreenViewableItemChange}
|
|
65
|
+
/>
|
|
66
|
+
) : null}
|
|
67
|
+
</>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
export default SimpleImageSlider;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React, { type PropsWithChildren, useMemo } from 'react';
|
|
2
|
+
import { type DefaultTheme, ThemeProvider } from 'styled-components/native';
|
|
3
|
+
|
|
4
|
+
export default function StyledComponentsThemeProvider({ children }: PropsWithChildren) {
|
|
5
|
+
const styles = useMemo(
|
|
6
|
+
() => ({
|
|
7
|
+
spacing: {
|
|
8
|
+
xxs: 2,
|
|
9
|
+
xs: 4,
|
|
10
|
+
s: 8,
|
|
11
|
+
m: 16,
|
|
12
|
+
l: 20,
|
|
13
|
+
xl: 40,
|
|
14
|
+
},
|
|
15
|
+
borderRadius: {
|
|
16
|
+
xs: 2,
|
|
17
|
+
s: 4,
|
|
18
|
+
m: 8,
|
|
19
|
+
l: 16,
|
|
20
|
+
xl: 24,
|
|
21
|
+
},
|
|
22
|
+
borderWidth: {
|
|
23
|
+
xs: 1,
|
|
24
|
+
s: 2,
|
|
25
|
+
m: 4,
|
|
26
|
+
l: 8,
|
|
27
|
+
xl: 16,
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
30
|
+
|
|
31
|
+
[]
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const theme: DefaultTheme = useMemo(
|
|
35
|
+
() => ({
|
|
36
|
+
colors: {
|
|
37
|
+
pageCounterBackground: '#D3D3D3',
|
|
38
|
+
pageCounterBorder: '#000000',
|
|
39
|
+
fullScreenCloseButton: '#FFFFFF',
|
|
40
|
+
descriptionContainerBorder: '#FFFFFF',
|
|
41
|
+
},
|
|
42
|
+
styles,
|
|
43
|
+
}),
|
|
44
|
+
[styles]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
|
|
48
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import StyledComponentsThemeProvider from './StyledComponentsThemeProvider';
|
|
2
|
+
import BaseListImageSlider, { type BaseSimpleImageSliderProps } from './BaseSimpleImageSlider';
|
|
3
|
+
import SimpleImageSlider, { type SimpleImageSliderProps } from './SimpleImageSlider';
|
|
4
|
+
import FullScreenImageSlider, { type FullScreenImageSliderProps } from './FullScreenImageSlider';
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
StyledComponentsThemeProvider as SimpleImageSliderThemeProvider,
|
|
8
|
+
BaseListImageSlider,
|
|
9
|
+
type BaseSimpleImageSliderProps,
|
|
10
|
+
SimpleImageSlider,
|
|
11
|
+
type SimpleImageSliderProps,
|
|
12
|
+
FullScreenImageSlider,
|
|
13
|
+
type FullScreenImageSliderProps,
|
|
14
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React, { isValidElement } from 'react';
|
|
2
|
+
|
|
3
|
+
export type RenderProp =
|
|
4
|
+
| React.ComponentType<unknown>
|
|
5
|
+
| React.ReactElement
|
|
6
|
+
| string
|
|
7
|
+
| undefined
|
|
8
|
+
| null;
|
|
9
|
+
|
|
10
|
+
export default function renderProp(Component: RenderProp) {
|
|
11
|
+
return Component ? (
|
|
12
|
+
typeof Component === 'string' ? (
|
|
13
|
+
Component
|
|
14
|
+
) : isValidElement(Component) ? (
|
|
15
|
+
Component
|
|
16
|
+
) : (
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
18
|
+
// @ts-ignore
|
|
19
|
+
<Component />
|
|
20
|
+
)
|
|
21
|
+
) : null;
|
|
22
|
+
}
|