@one-am/react-native-simple-image-slider 0.15.0 → 0.16.1

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 (58) hide show
  1. package/README.md +12 -6
  2. package/lib/commonjs/@types/pinch-to-zoom.js +0 -4
  3. package/lib/commonjs/AbsoluteComponentContainer.js +34 -0
  4. package/lib/commonjs/AbsoluteComponentContainer.js.map +1 -0
  5. package/lib/commonjs/BaseSimpleImageSlider.js +98 -135
  6. package/lib/commonjs/BaseSimpleImageSlider.js.map +1 -1
  7. package/lib/commonjs/FullScreenImageSlider.js +20 -29
  8. package/lib/commonjs/FullScreenImageSlider.js.map +1 -1
  9. package/lib/commonjs/PageCounter.js +24 -20
  10. package/lib/commonjs/PageCounter.js.map +1 -1
  11. package/lib/commonjs/PinchToZoom.js +5 -5
  12. package/lib/commonjs/PinchToZoom.js.map +1 -1
  13. package/lib/commonjs/SimpleImageSliderThemeProvider.js +19 -11
  14. package/lib/commonjs/SimpleImageSliderThemeProvider.js.map +1 -1
  15. package/lib/commonjs/index.js +9 -1
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/module/@types/pinch-to-zoom.js +0 -2
  18. package/lib/module/AbsoluteComponentContainer.js +28 -0
  19. package/lib/module/AbsoluteComponentContainer.js.map +1 -0
  20. package/lib/module/BaseSimpleImageSlider.js +99 -136
  21. package/lib/module/BaseSimpleImageSlider.js.map +1 -1
  22. package/lib/module/FullScreenImageSlider.js +21 -30
  23. package/lib/module/FullScreenImageSlider.js.map +1 -1
  24. package/lib/module/PageCounter.js +23 -20
  25. package/lib/module/PageCounter.js.map +1 -1
  26. package/lib/module/PinchToZoom.js +5 -5
  27. package/lib/module/PinchToZoom.js.map +1 -1
  28. package/lib/module/SimpleImageSliderThemeProvider.js +19 -12
  29. package/lib/module/SimpleImageSliderThemeProvider.js.map +1 -1
  30. package/lib/module/index.js +2 -2
  31. package/lib/module/index.js.map +1 -1
  32. package/lib/typescript/src/@types/pinch-to-zoom.d.ts +3 -4
  33. package/lib/typescript/src/@types/pinch-to-zoom.d.ts.map +1 -1
  34. package/lib/typescript/src/AbsoluteComponentContainer.d.ts +7 -0
  35. package/lib/typescript/src/AbsoluteComponentContainer.d.ts.map +1 -0
  36. package/lib/typescript/src/BaseSimpleImageSlider.d.ts +2 -2
  37. package/lib/typescript/src/BaseSimpleImageSlider.d.ts.map +1 -1
  38. package/lib/typescript/src/FullScreenImageSlider.d.ts.map +1 -1
  39. package/lib/typescript/src/PageCounter.d.ts.map +1 -1
  40. package/lib/typescript/src/PinchToZoom.d.ts.map +1 -1
  41. package/lib/typescript/src/SimpleImageSliderThemeProvider.d.ts +10 -2
  42. package/lib/typescript/src/SimpleImageSliderThemeProvider.d.ts.map +1 -1
  43. package/lib/typescript/src/index.d.ts +2 -3
  44. package/lib/typescript/src/index.d.ts.map +1 -1
  45. package/package.json +161 -160
  46. package/src/@types/pinch-to-zoom.ts +3 -5
  47. package/src/AbsoluteComponentContainer.tsx +30 -0
  48. package/src/BaseSimpleImageSlider.tsx +119 -157
  49. package/src/FullScreenImageSlider.tsx +39 -34
  50. package/src/PageCounter.tsx +35 -17
  51. package/src/PinchToZoom.tsx +10 -9
  52. package/src/SimpleImageSliderThemeProvider.tsx +32 -13
  53. package/src/index.tsx +7 -2
  54. package/lib/commonjs/@types/styled.d.js +0 -4
  55. package/lib/commonjs/@types/styled.d.js.map +0 -1
  56. package/lib/module/@types/styled.d.js +0 -4
  57. package/lib/module/@types/styled.d.js.map +0 -1
  58. package/src/@types/styled.d.ts +0 -17
@@ -9,10 +9,17 @@ import React, {
9
9
  } from 'react';
10
10
  import { FlashList, type ListRenderItemInfo } from '@shopify/flash-list';
11
11
  import mergeRefs from 'merge-refs';
12
- import { Image, type ImageProps, type ImageStyle } from 'expo-image';
13
- import { Pressable, type StyleProp, type TextStyle, type ViewStyle } from 'react-native';
12
+ import { Image, type ImageStyle } from 'expo-image';
13
+ import {
14
+ type LayoutChangeEvent,
15
+ Platform,
16
+ Pressable,
17
+ type StyleProp,
18
+ StyleSheet,
19
+ type TextStyle,
20
+ type ViewStyle,
21
+ } from 'react-native';
14
22
  import type ViewToken from '@shopify/flash-list/src/viewability/ViewToken';
15
- import styled from 'styled-components/native';
16
23
  import PageCounter, { type PageCounterProps } from './PageCounter';
17
24
  import PinchToZoom, { type PinchToZoomProps } from './PinchToZoom';
18
25
  import { GestureHandlerRootView, ScrollView } from 'react-native-gesture-handler';
@@ -20,6 +27,7 @@ import renderProp, { type RenderProp } from './utils/renderProp';
20
27
  import type { SimpleImageSliderItem } from './@types/slider';
21
28
  import type { PinchToZoomStatus } from './@types/pinch-to-zoom';
22
29
  import Animated from 'react-native-reanimated';
30
+ import { AbsoluteComponentContainer } from './AbsoluteComponentContainer';
23
31
 
24
32
  export type BaseSimpleImageSliderProps = {
25
33
  /**
@@ -78,7 +86,7 @@ export type BaseSimpleImageSliderProps = {
78
86
  */
79
87
  PageCounterComponent?: React.FunctionComponent<PageCounterProps>;
80
88
  /**
81
- * @description Callback that renders the page counter. If provided, this will replace the default page counter.
89
+ * @description Callback that renders the page counter. Overrides `PageCounterComponent` if provided.
82
90
  * @param currentPage The current page number.
83
91
  * @param totalPages The total number of pages.
84
92
  */
@@ -127,63 +135,12 @@ export type BaseSimpleImageSliderProps = {
127
135
  */
128
136
  sharedTransitionTag?: string;
129
137
  /**
130
- * @description Style that will be applied to ebvery image in the slider.
138
+ * @description Style that will be applied to every image in the slider.
131
139
  */
132
140
  imageStyle?: StyleProp<ImageStyle>;
133
141
  };
134
142
 
135
- const StyledAbsoluteComponentContainer = styled.View<{
136
- position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
137
- }>`
138
- z-index: 1000;
139
- position: absolute;
140
- bottom: ${({ position }) =>
141
- position === 'bottom-left' || position === 'bottom-right' ? `16px` : 'auto'};
142
- top: ${({ position }) =>
143
- position === 'top-left' || position === 'top-right' ? `16px` : 'auto'};
144
- left: ${({ position }) =>
145
- position === 'top-left' || position === 'bottom-left' ? `16px` : 'auto'};
146
- right: ${({ position }) =>
147
- position === 'top-right' || position === 'bottom-right' ? `16px` : 'auto'};
148
- `;
149
-
150
- const StyledPageCounter = styled(PageCounter)<{
151
- position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
152
- }>`
153
- z-index: 1000;
154
- position: absolute;
155
- bottom: ${({ position }) =>
156
- position === 'bottom-left' || position === 'bottom-right' ? `16px` : 'auto'};
157
- top: ${({ position }) =>
158
- position === 'top-left' || position === 'top-right' ? `16px` : 'auto'};
159
- left: ${({ position }) =>
160
- position === 'top-left' || position === 'bottom-left' ? `16px` : 'auto'};
161
- right: ${({ position }) =>
162
- position === 'top-right' || position === 'bottom-right' ? `16px` : 'auto'};
163
- `;
164
-
165
- const StyledContainer = styled(GestureHandlerRootView)<{ aspectRatio: number }>`
166
- aspect-ratio: ${({ aspectRatio }) => aspectRatio ?? 4 / 3};
167
- width: 100%;
168
- `;
169
-
170
- const StyledImage = styled(Image)<
171
- ImageProps & {
172
- imageWidth?: number;
173
- imageHeight?: number;
174
- imageAspectRatio: number;
175
- }
176
- >`
177
- width: ${({ imageWidth }) => (imageWidth ? `${imageWidth}px` : '100%')};
178
- height: ${({ imageHeight }) => (imageHeight ? `${imageHeight}px` : '100%')};
179
- aspect-ratio: ${({ imageAspectRatio }) => imageAspectRatio};
180
- `;
181
-
182
- const AnimatedStyledImage = Animated.createAnimatedComponent(StyledImage);
183
-
184
- const StyledPinchToZoom = styled(PinchToZoom)`
185
- z-index: 1000;
186
- `;
143
+ const AnimatedImage = Animated.createAnimatedComponent(Image);
187
144
 
188
145
  /**
189
146
  * @description A simple image slider that displays a list of images. This is the component
@@ -206,7 +163,7 @@ const BaseSimpleImageSlider = forwardRef<
206
163
  pageCounterPosition = 'bottom-left',
207
164
  pageCounterStyle,
208
165
  pageCounterTextStyle,
209
- PageCounterComponent,
166
+ PageCounterComponent = PageCounter,
210
167
  renderPageCounter,
211
168
  TopRightComponent,
212
169
  TopLeftComponent,
@@ -222,36 +179,55 @@ const BaseSimpleImageSlider = forwardRef<
222
179
  },
223
180
  ref
224
181
  ) {
225
- if (renderPageCounter !== undefined && PageCounterComponent !== undefined) {
226
- throw new Error(
227
- 'You should provide either `renderPageCounter` or `PageCounterComponent`, not both.'
228
- );
229
- }
230
-
231
- const ActualPageCounterComponent = PageCounterComponent
232
- ? makeStyledPageCounter(PageCounterComponent)
233
- : StyledPageCounter;
234
-
235
182
  const listRef = useRef<FlashList<SimpleImageSliderItem>>(null);
236
- const [currentItem, setCurrentItem] = useState(0);
237
183
 
184
+ const styles = useMemo(
185
+ () => makeStyles({ imageAspectRatio, imageWidth, imageHeight }),
186
+ [imageAspectRatio, imageHeight, imageWidth]
187
+ );
238
188
  const slicedData = useMemo(
239
189
  () => (maxItems !== undefined ? (data?.slice(0, maxItems) ?? []) : (data ?? [])),
240
190
  [data, maxItems]
241
191
  );
192
+ const estimatedItemSize = useMemo(() => {
193
+ return imageWidth ?? (imageHeight ? imageHeight * (imageAspectRatio ?? 4 / 3) : 350);
194
+ }, [imageAspectRatio, imageHeight, imageWidth]);
242
195
 
243
- useEffect(() => {
244
- setCurrentItem(indexOverride ?? 0);
196
+ const [currentItem, setCurrentItem] = useState(0);
197
+ const [scrollEnabled, setScrollEnabled] = useState(true);
198
+ const [snapToInterval, setSnapToInterval] = useState<number | undefined>(undefined);
245
199
 
246
- listRef.current?.scrollToIndex({ index: indexOverride ?? 0, animated: false });
247
- }, [indexOverride, slicedData]);
200
+ const handleScaleChange = useCallback(() => {
201
+ setScrollEnabled(false);
202
+ }, []);
248
203
 
249
- const keyExtractor = useCallback((item: SimpleImageSliderItem) => item.key, []);
204
+ const handleScaleReset = useCallback(() => {
205
+ setScrollEnabled(true);
206
+ }, []);
207
+
208
+ const handleViewableItemsChanged = useCallback(
209
+ ({ viewableItems }: { viewableItems: ViewToken[]; changed: ViewToken[] }) => {
210
+ const newIndex = viewableItems[0]?.index;
211
+ if (newIndex !== undefined && newIndex !== null) {
212
+ setCurrentItem(newIndex);
213
+ onViewableItemChange?.(newIndex);
214
+ }
215
+ },
216
+ [onViewableItemChange]
217
+ );
218
+
219
+ const handlePinchToZoomStatusChange = useCallback(
220
+ (status: PinchToZoomStatus) => {
221
+ listRef.current?.recordInteraction();
222
+ onPinchToZoomStatusChange?.(status);
223
+ },
224
+ [onPinchToZoomStatusChange]
225
+ );
250
226
 
251
227
  const renderItem = useCallback(
252
228
  ({ item, index }: ListRenderItemInfo<SimpleImageSliderItem>) => {
253
229
  const ImageComponent =
254
- sharedTransitionTag && index === currentItem ? AnimatedStyledImage : StyledImage;
230
+ sharedTransitionTag && index === currentItem ? AnimatedImage : Image;
255
231
 
256
232
  return (
257
233
  <Pressable
@@ -263,78 +239,53 @@ const BaseSimpleImageSlider = forwardRef<
263
239
  <ImageComponent
264
240
  sharedTransitionTag={sharedTransitionTag}
265
241
  transition={200}
242
+ // https://github.com/expo/expo/issues/34810
243
+ /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment */
266
244
  placeholder={item.placeholder}
267
245
  placeholderContentFit={'cover'}
268
- imageWidth={imageWidth}
269
- imageHeight={imageHeight}
270
- imageAspectRatio={imageAspectRatio}
271
246
  recyclingKey={item.key}
247
+ // https://github.com/expo/expo/issues/34810
248
+ /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment */
272
249
  source={item.source}
273
250
  contentFit={'cover'}
274
251
  contentPosition={'center'}
275
- style={imageStyle}
252
+ style={[styles.image, imageStyle]}
276
253
  />
277
254
  </Pressable>
278
255
  );
279
256
  },
280
- [
281
- currentItem,
282
- imageAspectRatio,
283
- imageHeight,
284
- imageStyle,
285
- imageWidth,
286
- onItemPress,
287
- sharedTransitionTag,
288
- ]
257
+ [currentItem, imageStyle, onItemPress, sharedTransitionTag, styles.image]
289
258
  );
290
259
 
291
- const onViewableItemsChanged = useCallback(
292
- ({ viewableItems }: { viewableItems: ViewToken[]; changed: ViewToken[] }) => {
293
- const newIndex = viewableItems[0]?.index;
294
- if (newIndex !== undefined && newIndex !== null) {
295
- setCurrentItem(newIndex);
296
- onViewableItemChange?.(newIndex);
297
- }
298
- },
299
- [onViewableItemChange]
300
- );
301
-
302
- const [scrollEnabled, setScrollEnabled] = useState(true);
303
-
304
- const onScaleChange = useCallback(() => {
305
- setScrollEnabled(false);
306
- }, []);
260
+ const keyExtractor = useCallback((item: SimpleImageSliderItem) => item.key, []);
307
261
 
308
- const onScaleReset = useCallback(() => {
309
- setScrollEnabled(true);
262
+ const handleListLayout = useCallback((event: LayoutChangeEvent) => {
263
+ if (Platform.OS === 'ios') {
264
+ // temporary workaround for iOS scrolling a bit more than expected
265
+ setSnapToInterval(event.nativeEvent.layout.width - 0.5);
266
+ }
310
267
  }, []);
311
268
 
312
- const estimatedItemSize = useMemo(() => {
313
- return imageWidth ?? (imageHeight ? imageHeight * (imageAspectRatio ?? 4 / 3) : 350);
314
- }, [imageAspectRatio, imageHeight, imageWidth]);
269
+ useEffect(() => {
270
+ setCurrentItem(indexOverride ?? 0);
315
271
 
316
- const internalOnPinchToZoomStatusChange = useCallback(
317
- (status: PinchToZoomStatus) => {
318
- listRef.current?.recordInteraction();
319
- onPinchToZoomStatusChange?.(status);
320
- },
321
- [onPinchToZoomStatusChange]
322
- );
272
+ listRef.current?.scrollToIndex({ index: indexOverride ?? 0, animated: false });
273
+ }, [indexOverride, slicedData]);
323
274
 
324
275
  const list = (
325
276
  <FlashList
326
- // @ts-expect-error - there's just a small inconsistency with hitSlop typing
327
277
  renderScrollComponent={ScrollView}
328
278
  scrollEnabled={scrollEnabled}
329
279
  disableScrollViewPanResponder={enablePinchToZoom ? !scrollEnabled : false}
330
280
  ref={mergeRefs(ref, listRef)}
331
281
  initialScrollIndex={indexOverride ?? currentItem ?? 0}
332
- onViewableItemsChanged={onViewableItemsChanged}
282
+ onViewableItemsChanged={handleViewableItemsChanged}
333
283
  viewabilityConfig={{
334
284
  itemVisiblePercentThreshold: 55,
335
285
  }}
336
286
  decelerationRate={'fast'}
337
- pagingEnabled={true}
287
+ pagingEnabled={snapToInterval === undefined}
288
+ snapToInterval={snapToInterval}
338
289
  showsHorizontalScrollIndicator={false}
339
290
  showsVerticalScrollIndicator={false}
340
291
  horizontal={true}
@@ -346,22 +297,24 @@ const BaseSimpleImageSlider = forwardRef<
346
297
  width: estimatedItemSize,
347
298
  height: imageHeight ?? estimatedItemSize / imageAspectRatio,
348
299
  }}
300
+ onLayout={handleListLayout}
349
301
  />
350
302
  );
351
303
 
352
304
  return (
353
- <StyledContainer aspectRatio={imageAspectRatio} style={style}>
305
+ <GestureHandlerRootView style={[styles.container, style]}>
354
306
  {enablePinchToZoom ? (
355
- <StyledPinchToZoom
307
+ <PinchToZoom
308
+ style={styles.pinchToZoom}
356
309
  onDismiss={onPinchToZoomRequestClose}
357
- onStatusChange={internalOnPinchToZoomStatusChange}
358
- onScaleChange={onScaleChange}
359
- onScaleReset={onScaleReset}
310
+ onStatusChange={handlePinchToZoomStatusChange}
311
+ onScaleChange={handleScaleChange}
312
+ onScaleReset={handleScaleReset}
360
313
  maximumZoomScale={5}
361
314
  minimumZoomScale={1}
362
315
  >
363
316
  {list}
364
- </StyledPinchToZoom>
317
+ </PinchToZoom>
365
318
  ) : (
366
319
  list
367
320
  )}
@@ -369,54 +322,63 @@ const BaseSimpleImageSlider = forwardRef<
369
322
  renderPageCounter ? (
370
323
  renderPageCounter(currentItem + 1, slicedData.length)
371
324
  ) : (
372
- <ActualPageCounterComponent
373
- position={pageCounterPosition}
374
- totalPages={slicedData.length}
375
- currentPage={currentItem + 1}
376
- style={pageCounterStyle}
377
- textStyle={pageCounterTextStyle}
378
- />
325
+ <AbsoluteComponentContainer position={pageCounterPosition}>
326
+ <PageCounterComponent
327
+ totalPages={slicedData.length}
328
+ currentPage={currentItem + 1}
329
+ style={pageCounterStyle}
330
+ textStyle={pageCounterTextStyle}
331
+ />
332
+ </AbsoluteComponentContainer>
379
333
  )
380
334
  ) : null}
381
335
  {TopRightComponent ? (
382
- <StyledAbsoluteComponentContainer position={'top-right'}>
336
+ <AbsoluteComponentContainer position={'top-right'}>
383
337
  {renderProp(TopRightComponent)}
384
- </StyledAbsoluteComponentContainer>
338
+ </AbsoluteComponentContainer>
385
339
  ) : null}
386
340
  {TopLeftComponent ? (
387
- <StyledAbsoluteComponentContainer position={'top-left'}>
341
+ <AbsoluteComponentContainer position={'top-left'}>
388
342
  {renderProp(TopLeftComponent)}
389
- </StyledAbsoluteComponentContainer>
343
+ </AbsoluteComponentContainer>
390
344
  ) : null}
391
345
  {BottomRightComponent ? (
392
- <StyledAbsoluteComponentContainer position={'bottom-right'}>
346
+ <AbsoluteComponentContainer position={'bottom-right'}>
393
347
  {renderProp(BottomRightComponent)}
394
- </StyledAbsoluteComponentContainer>
348
+ </AbsoluteComponentContainer>
395
349
  ) : null}
396
350
  {BottomLeftComponent ? (
397
- <StyledAbsoluteComponentContainer position={'bottom-left'}>
351
+ <AbsoluteComponentContainer position={'bottom-left'}>
398
352
  {renderProp(BottomLeftComponent)}
399
- </StyledAbsoluteComponentContainer>
353
+ </AbsoluteComponentContainer>
400
354
  ) : null}
401
- </StyledContainer>
355
+ </GestureHandlerRootView>
402
356
  );
403
357
  });
404
358
 
405
- const makeStyledPageCounter = (
406
- PageCounterComponent: React.FunctionComponent<PageCounterProps>
407
- ) => styled(PageCounterComponent)<{
408
- position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
409
- }>`
410
- z-index: 1000;
411
- position: absolute;
412
- bottom: ${({ position }) =>
413
- position === 'bottom-left' || position === 'bottom-right' ? `16px` : 'auto'};
414
- top: ${({ position }) =>
415
- position === 'top-left' || position === 'top-right' ? `16px` : 'auto'};
416
- left: ${({ position }) =>
417
- position === 'top-left' || position === 'bottom-left' ? `16px` : 'auto'};
418
- right: ${({ position }) =>
419
- position === 'top-right' || position === 'bottom-right' ? `16px` : 'auto'};
420
- `;
359
+ const makeStyles = ({
360
+ imageAspectRatio,
361
+ imageWidth,
362
+ imageHeight,
363
+ }: {
364
+ imageAspectRatio?: number;
365
+ imageWidth?: number;
366
+ imageHeight?: number;
367
+ }) => {
368
+ return StyleSheet.create({
369
+ container: {
370
+ width: '100%',
371
+ aspectRatio: imageAspectRatio ?? 4 / 3,
372
+ },
373
+ pinchToZoom: {
374
+ zIndex: 1000,
375
+ },
376
+ image: {
377
+ width: imageWidth ?? '100%',
378
+ height: imageHeight ?? '100%',
379
+ aspectRatio: imageAspectRatio ?? 4 / 3,
380
+ },
381
+ });
382
+ };
421
383
 
422
384
  export default BaseSimpleImageSlider;
@@ -6,7 +6,14 @@ import React, {
6
6
  useMemo,
7
7
  useState,
8
8
  } from 'react';
9
- import { Modal, type ScaledSize, StyleSheet, useWindowDimensions } from 'react-native';
9
+ import {
10
+ Modal,
11
+ type ScaledSize,
12
+ StyleSheet,
13
+ TouchableOpacity,
14
+ useWindowDimensions,
15
+ View,
16
+ } from 'react-native';
10
17
  import IconX from './icons/IconX';
11
18
  import Animated, {
12
19
  runOnJS,
@@ -15,13 +22,16 @@ import Animated, {
15
22
  withTiming,
16
23
  } from 'react-native-reanimated';
17
24
  import { setStatusBarStyle } from 'expo-status-bar';
18
- import styled, { useTheme } from 'styled-components/native';
19
25
  import { FlashList } from '@shopify/flash-list';
20
26
  import BaseListImageSlider, { type BaseSimpleImageSliderProps } from './BaseSimpleImageSlider';
21
27
  import { type EdgeInsets, useSafeAreaInsets } from 'react-native-safe-area-context';
22
28
  import type { PinchToZoomStatus } from './@types/pinch-to-zoom';
23
29
  import type { SimpleImageSliderItem } from './@types/slider';
24
30
  import renderProp, { type RenderProp } from './utils/renderProp';
31
+ import {
32
+ type SimpleImageSliderTheme,
33
+ useSimpleImageSliderTheme,
34
+ } from './SimpleImageSliderThemeProvider';
25
35
 
26
36
  export type FullScreenImageSliderProps = Omit<BaseSimpleImageSliderProps, 'imageWidth'> & {
27
37
  /**
@@ -48,25 +58,6 @@ export type FullScreenImageSliderProps = Omit<BaseSimpleImageSliderProps, 'image
48
58
  onFadeOut?: () => void;
49
59
  };
50
60
 
51
- const StyledDescriptionContainer = styled.View`
52
- position: absolute;
53
- border-top-width: 1px;
54
- border-top-color: ${({ theme }) => theme.colors.simpleImageSlider.descriptionContainerBorder};
55
- width: 100%;
56
- padding-top: 20px;
57
- `;
58
-
59
- const StyledModalCloseButton = styled.TouchableOpacity`
60
- position: absolute;
61
- z-index: 1000;
62
- `;
63
-
64
- const StyledModalContentContainer = styled(Animated.View)`
65
- align-items: center;
66
- justify-content: center;
67
- gap: 16px;
68
- `;
69
-
70
61
  /**
71
62
  * @description A full screen image slider that displays images in a modal.
72
63
  */
@@ -87,11 +78,11 @@ const FullScreenImageSlider = forwardRef<
87
78
  ref
88
79
  ) {
89
80
  const windowDimensions = useWindowDimensions();
90
- const theme = useTheme();
81
+ const theme = useSimpleImageSliderTheme();
91
82
  const safeAreaInsets = useSafeAreaInsets();
92
83
  const styles = useMemo(
93
- () => makeStyles(safeAreaInsets, windowDimensions),
94
- [safeAreaInsets, windowDimensions]
84
+ () => makeStyles(safeAreaInsets, windowDimensions, theme),
85
+ [safeAreaInsets, windowDimensions, theme]
95
86
  );
96
87
 
97
88
  const [internalIndex, setInternalIndex] = useState<number>(0);
@@ -122,8 +113,8 @@ const FullScreenImageSlider = forwardRef<
122
113
 
123
114
  const onPinchToZoomStatusChange = useCallback(
124
115
  ({ translation, scale }: PinchToZoomStatus) => {
125
- if (scale.value <= 1) {
126
- if (translation.x.value === 0 && translation.y.value === 0) {
116
+ if (scale <= 1) {
117
+ if (translation.x === 0 && translation.y === 0) {
127
118
  runOnJS(setStatusBarStyle)('light');
128
119
  backgroundOpacity.value = withTiming(1);
129
120
  } else {
@@ -147,14 +138,14 @@ const FullScreenImageSlider = forwardRef<
147
138
  transparent={true}
148
139
  visible={open}
149
140
  >
150
- <StyledModalContentContainer style={[styles.modalContent, modalContentStyle]}>
151
- <StyledModalCloseButton style={styles.closeButton} onPress={onRequestClose}>
141
+ <Animated.View style={[styles.modalContent, modalContentStyle]}>
142
+ <TouchableOpacity style={styles.closeButton} onPress={onRequestClose}>
152
143
  {CloseButtonIcon ? (
153
144
  renderProp(CloseButtonIcon)
154
145
  ) : (
155
- <IconX color={theme.colors.simpleImageSlider.fullScreenCloseButton} />
146
+ <IconX color={theme.colors.fullScreenCloseButton} />
156
147
  )}
157
- </StyledModalCloseButton>
148
+ </TouchableOpacity>
158
149
  <BaseListImageSlider
159
150
  data={data}
160
151
  enablePinchToZoom={true}
@@ -168,28 +159,42 @@ const FullScreenImageSlider = forwardRef<
168
159
  />
169
160
 
170
161
  {renderDescription && data[internalIndex] ? (
171
- <StyledDescriptionContainer style={styles.descriptionContainer}>
162
+ <View style={styles.descriptionContainer}>
172
163
  {renderDescription(data[internalIndex], internalIndex)}
173
- </StyledDescriptionContainer>
164
+ </View>
174
165
  ) : null}
175
- </StyledModalContentContainer>
166
+ </Animated.View>
176
167
  </Modal>
177
168
  );
178
169
  });
179
170
 
180
171
  export default FullScreenImageSlider;
181
172
 
182
- const makeStyles = (safeAreaInsets: EdgeInsets, windowDimensions: ScaledSize) => {
173
+ const makeStyles = (
174
+ safeAreaInsets: EdgeInsets,
175
+ windowDimensions: ScaledSize,
176
+ theme: SimpleImageSliderTheme
177
+ ) => {
183
178
  return StyleSheet.create({
184
179
  closeButton: {
180
+ position: 'absolute',
181
+ zIndex: 1000,
185
182
  top: safeAreaInsets.top,
186
183
  right: safeAreaInsets.right + 20,
187
184
  },
188
185
  modalContent: {
186
+ alignItems: 'center',
187
+ justifyContent: 'center',
188
+ gap: 16,
189
189
  height: windowDimensions.height,
190
190
  width: windowDimensions.width,
191
191
  },
192
192
  descriptionContainer: {
193
+ position: 'absolute',
194
+ borderTopWidth: 1,
195
+ borderTopColor: theme.colors.descriptionContainerBorder,
196
+ width: '100%',
197
+ paddingTop: 20,
193
198
  bottom: safeAreaInsets.bottom + 100,
194
199
  paddingLeft: safeAreaInsets.left + 20,
195
200
  paddingRight: safeAreaInsets.right + 20,
@@ -1,6 +1,16 @@
1
- import React from 'react';
2
- import { type StyleProp, Text, type TextStyle, type ViewStyle } from 'react-native';
3
- import styled from 'styled-components/native';
1
+ import React, { useMemo } from 'react';
2
+ import {
3
+ type StyleProp,
4
+ StyleSheet,
5
+ Text,
6
+ type TextStyle,
7
+ View,
8
+ type ViewStyle,
9
+ } from 'react-native';
10
+ import {
11
+ type SimpleImageSliderTheme,
12
+ useSimpleImageSliderTheme,
13
+ } from './SimpleImageSliderThemeProvider';
4
14
 
5
15
  export type PageCounterProps = {
6
16
  /**
@@ -21,29 +31,37 @@ export type PageCounterProps = {
21
31
  textStyle?: StyleProp<TextStyle>;
22
32
  };
23
33
 
24
- const StyledContainer = styled.View`
25
- background-color: ${({ theme }) => theme.colors.simpleImageSlider.pageCounterBackground};
26
- border-width: 1px;
27
- border-color: ${({ theme }) => theme.colors.simpleImageSlider.pageCounterBorder};
28
- border-radius: 8px;
29
- padding: 6px 5px;
30
- width: 75px;
31
- flex-direction: row;
32
- align-items: center;
33
- justify-content: center;
34
- `;
35
-
36
34
  export default function PageCounter({
37
35
  currentPage,
38
36
  totalPages,
39
37
  style,
40
38
  textStyle,
41
39
  }: PageCounterProps) {
40
+ const theme = useSimpleImageSliderTheme();
41
+ const styles = useMemo(() => makeStyles(theme), [theme]);
42
+
42
43
  return (
43
- <StyledContainer style={style}>
44
+ <View style={[styles.container, style]}>
44
45
  <Text style={textStyle}>
45
46
  {currentPage} / {totalPages}
46
47
  </Text>
47
- </StyledContainer>
48
+ </View>
48
49
  );
49
50
  }
51
+
52
+ const makeStyles = (theme: SimpleImageSliderTheme) => {
53
+ return StyleSheet.create({
54
+ container: {
55
+ backgroundColor: theme.colors.pageCounterBackground,
56
+ borderWidth: 1,
57
+ borderColor: theme.colors.pageCounterBorder,
58
+ borderRadius: 8,
59
+ paddingVertical: 6,
60
+ paddingHorizontal: 5,
61
+ width: 75,
62
+ flexDirection: 'row',
63
+ alignItems: 'center',
64
+ justifyContent: 'center',
65
+ },
66
+ });
67
+ };
@@ -126,6 +126,7 @@ export default function PinchToZoom({
126
126
  (-viewWidth.value * (scale.value - minimumZoomScale)) / 2,
127
127
  (viewWidth.value * (scale.value - minimumZoomScale)) / 2
128
128
  );
129
+
129
130
  translationY.value = clamp(
130
131
  prevTranslationY.value +
131
132
  -1 *
@@ -173,8 +174,8 @@ export default function PinchToZoom({
173
174
  originY,
174
175
  prevTranslationX,
175
176
  prevTranslationY,
176
- viewWidth.value,
177
- viewHeight.value,
177
+ viewWidth,
178
+ viewHeight,
178
179
  minimumZoomScale,
179
180
  onDismiss,
180
181
  onScaleReset,
@@ -247,15 +248,15 @@ export default function PinchToZoom({
247
248
  disabled,
248
249
  minimumZoomScale,
249
250
  onDismiss,
250
- prevScale.value,
251
+ prevScale,
251
252
  prevTranslationX,
252
253
  prevTranslationY,
253
- scale.value,
254
+ scale,
254
255
  windowHeight,
255
256
  translationX,
256
257
  translationY,
257
- viewHeight.value,
258
- viewWidth.value,
258
+ viewHeight,
259
+ viewWidth,
259
260
  ]
260
261
  );
261
262
 
@@ -298,10 +299,10 @@ export default function PinchToZoom({
298
299
  useAnimatedReaction(
299
300
  () => {
300
301
  return {
301
- scale: scale,
302
+ scale: scale.value,
302
303
  translation: {
303
- x: translationX,
304
- y: translationY,
304
+ x: translationX.value,
305
+ y: translationY.value,
305
306
  },
306
307
  };
307
308
  },