@one-am/react-native-simple-image-slider 0.14.0 → 0.16.0

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 (90) hide show
  1. package/README.md +15 -9
  2. package/lib/commonjs/@types/common.js.map +1 -1
  3. package/lib/commonjs/@types/icons.js.map +1 -1
  4. package/lib/commonjs/@types/pinch-to-zoom.js +0 -4
  5. package/lib/commonjs/@types/pinch-to-zoom.js.map +1 -1
  6. package/lib/commonjs/@types/slider.js.map +1 -1
  7. package/lib/commonjs/AbsoluteComponentContainer.js +34 -0
  8. package/lib/commonjs/AbsoluteComponentContainer.js.map +1 -0
  9. package/lib/commonjs/BaseSimpleImageSlider.js +129 -157
  10. package/lib/commonjs/BaseSimpleImageSlider.js.map +1 -1
  11. package/lib/commonjs/FullScreenImageSlider.js +46 -51
  12. package/lib/commonjs/FullScreenImageSlider.js.map +1 -1
  13. package/lib/commonjs/PageCounter.js +30 -23
  14. package/lib/commonjs/PageCounter.js.map +1 -1
  15. package/lib/commonjs/PinchToZoom.js +16 -13
  16. package/lib/commonjs/PinchToZoom.js.map +1 -1
  17. package/lib/commonjs/SimpleImageSlider.js +31 -25
  18. package/lib/commonjs/SimpleImageSlider.js.map +1 -1
  19. package/lib/commonjs/SimpleImageSliderThemeProvider.js +26 -20
  20. package/lib/commonjs/SimpleImageSliderThemeProvider.js.map +1 -1
  21. package/lib/commonjs/icons/IconX.js +16 -14
  22. package/lib/commonjs/icons/IconX.js.map +1 -1
  23. package/lib/commonjs/index.js +10 -2
  24. package/lib/commonjs/index.js.map +1 -1
  25. package/lib/commonjs/package.json +1 -0
  26. package/lib/commonjs/utils/clamp.js.map +1 -1
  27. package/lib/commonjs/utils/renderProp.js +3 -2
  28. package/lib/commonjs/utils/renderProp.js.map +1 -1
  29. package/lib/module/@types/common.js +1 -1
  30. package/lib/module/@types/common.js.map +1 -1
  31. package/lib/module/@types/icons.js +2 -0
  32. package/lib/module/@types/icons.js.map +1 -1
  33. package/lib/module/@types/pinch-to-zoom.js +1 -1
  34. package/lib/module/@types/pinch-to-zoom.js.map +1 -1
  35. package/lib/module/@types/slider.js +2 -0
  36. package/lib/module/@types/slider.js.map +1 -1
  37. package/lib/module/AbsoluteComponentContainer.js +28 -0
  38. package/lib/module/AbsoluteComponentContainer.js.map +1 -0
  39. package/lib/module/BaseSimpleImageSlider.js +130 -156
  40. package/lib/module/BaseSimpleImageSlider.js.map +1 -1
  41. package/lib/module/FullScreenImageSlider.js +47 -50
  42. package/lib/module/FullScreenImageSlider.js.map +1 -1
  43. package/lib/module/PageCounter.js +31 -23
  44. package/lib/module/PageCounter.js.map +1 -1
  45. package/lib/module/PinchToZoom.js +17 -12
  46. package/lib/module/PinchToZoom.js.map +1 -1
  47. package/lib/module/SimpleImageSlider.js +31 -23
  48. package/lib/module/SimpleImageSlider.js.map +1 -1
  49. package/lib/module/SimpleImageSliderThemeProvider.js +27 -20
  50. package/lib/module/SimpleImageSliderThemeProvider.js.map +1 -1
  51. package/lib/module/icons/IconX.js +16 -12
  52. package/lib/module/icons/IconX.js.map +1 -1
  53. package/lib/module/index.js +4 -2
  54. package/lib/module/index.js.map +1 -1
  55. package/lib/module/package.json +1 -0
  56. package/lib/module/utils/clamp.js +2 -0
  57. package/lib/module/utils/clamp.js.map +1 -1
  58. package/lib/module/utils/renderProp.js +4 -1
  59. package/lib/module/utils/renderProp.js.map +1 -1
  60. package/lib/typescript/src/@types/pinch-to-zoom.d.ts +3 -4
  61. package/lib/typescript/src/@types/pinch-to-zoom.d.ts.map +1 -1
  62. package/lib/typescript/src/AbsoluteComponentContainer.d.ts +7 -0
  63. package/lib/typescript/src/AbsoluteComponentContainer.d.ts.map +1 -0
  64. package/lib/typescript/src/BaseSimpleImageSlider.d.ts +1 -1
  65. package/lib/typescript/src/BaseSimpleImageSlider.d.ts.map +1 -1
  66. package/lib/typescript/src/FullScreenImageSlider.d.ts +4 -4
  67. package/lib/typescript/src/FullScreenImageSlider.d.ts.map +1 -1
  68. package/lib/typescript/src/PageCounter.d.ts.map +1 -1
  69. package/lib/typescript/src/PinchToZoom.d.ts.map +1 -1
  70. package/lib/typescript/src/SimpleImageSlider.d.ts +15 -3
  71. package/lib/typescript/src/SimpleImageSlider.d.ts.map +1 -1
  72. package/lib/typescript/src/SimpleImageSliderThemeProvider.d.ts +10 -2
  73. package/lib/typescript/src/SimpleImageSliderThemeProvider.d.ts.map +1 -1
  74. package/lib/typescript/src/index.d.ts +2 -3
  75. package/lib/typescript/src/index.d.ts.map +1 -1
  76. package/package.json +161 -161
  77. package/src/@types/pinch-to-zoom.ts +3 -5
  78. package/src/AbsoluteComponentContainer.tsx +30 -0
  79. package/src/BaseSimpleImageSlider.tsx +119 -150
  80. package/src/FullScreenImageSlider.tsx +40 -38
  81. package/src/PageCounter.tsx +35 -17
  82. package/src/PinchToZoom.tsx +10 -9
  83. package/src/SimpleImageSlider.tsx +10 -0
  84. package/src/SimpleImageSliderThemeProvider.tsx +32 -13
  85. package/src/index.tsx +7 -2
  86. package/lib/commonjs/@types/styled.d.js +0 -4
  87. package/lib/commonjs/@types/styled.d.js.map +0 -1
  88. package/lib/module/@types/styled.d.js +0 -2
  89. package/lib/module/@types/styled.d.js.map +0 -1
  90. 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
  /**
@@ -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,
@@ -228,30 +185,55 @@ const BaseSimpleImageSlider = forwardRef<
228
185
  );
229
186
  }
230
187
 
231
- const ActualPageCounterComponent = PageCounterComponent
232
- ? makeStyledPageCounter(PageCounterComponent)
233
- : StyledPageCounter;
234
-
235
188
  const listRef = useRef<FlashList<SimpleImageSliderItem>>(null);
236
- const [currentItem, setCurrentItem] = useState(0);
237
189
 
190
+ const styles = useMemo(
191
+ () => makeStyles({ imageAspectRatio, imageWidth, imageHeight }),
192
+ [imageAspectRatio, imageHeight, imageWidth]
193
+ );
238
194
  const slicedData = useMemo(
239
- () => (maxItems !== undefined ? data?.slice(0, maxItems) ?? [] : data ?? []),
195
+ () => (maxItems !== undefined ? (data?.slice(0, maxItems) ?? []) : (data ?? [])),
240
196
  [data, maxItems]
241
197
  );
198
+ const estimatedItemSize = useMemo(() => {
199
+ return imageWidth ?? (imageHeight ? imageHeight * (imageAspectRatio ?? 4 / 3) : 350);
200
+ }, [imageAspectRatio, imageHeight, imageWidth]);
242
201
 
243
- useEffect(() => {
244
- setCurrentItem(indexOverride ?? 0);
202
+ const [currentItem, setCurrentItem] = useState(0);
203
+ const [scrollEnabled, setScrollEnabled] = useState(true);
204
+ const [snapToInterval, setSnapToInterval] = useState<number | undefined>(undefined);
245
205
 
246
- listRef.current?.scrollToIndex({ index: indexOverride ?? 0, animated: false });
247
- }, [indexOverride, slicedData]);
206
+ const handleScaleChange = useCallback(() => {
207
+ setScrollEnabled(false);
208
+ }, []);
248
209
 
249
- const keyExtractor = useCallback((item: SimpleImageSliderItem) => item.key, []);
210
+ const handleScaleReset = useCallback(() => {
211
+ setScrollEnabled(true);
212
+ }, []);
213
+
214
+ const handleViewableItemsChanged = useCallback(
215
+ ({ viewableItems }: { viewableItems: ViewToken[]; changed: ViewToken[] }) => {
216
+ const newIndex = viewableItems[0]?.index;
217
+ if (newIndex !== undefined && newIndex !== null) {
218
+ setCurrentItem(newIndex);
219
+ onViewableItemChange?.(newIndex);
220
+ }
221
+ },
222
+ [onViewableItemChange]
223
+ );
224
+
225
+ const handlePinchToZoomStatusChange = useCallback(
226
+ (status: PinchToZoomStatus) => {
227
+ listRef.current?.recordInteraction();
228
+ onPinchToZoomStatusChange?.(status);
229
+ },
230
+ [onPinchToZoomStatusChange]
231
+ );
250
232
 
251
233
  const renderItem = useCallback(
252
234
  ({ item, index }: ListRenderItemInfo<SimpleImageSliderItem>) => {
253
235
  const ImageComponent =
254
- sharedTransitionTag && index === currentItem ? AnimatedStyledImage : StyledImage;
236
+ sharedTransitionTag && index === currentItem ? AnimatedImage : Image;
255
237
 
256
238
  return (
257
239
  <Pressable
@@ -263,63 +245,38 @@ const BaseSimpleImageSlider = forwardRef<
263
245
  <ImageComponent
264
246
  sharedTransitionTag={sharedTransitionTag}
265
247
  transition={200}
248
+ // https://github.com/expo/expo/issues/34810
249
+ /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment */
266
250
  placeholder={item.placeholder}
267
251
  placeholderContentFit={'cover'}
268
- imageWidth={imageWidth}
269
- imageHeight={imageHeight}
270
- imageAspectRatio={imageAspectRatio}
271
252
  recyclingKey={item.key}
253
+ // https://github.com/expo/expo/issues/34810
254
+ /* eslint-disable-next-line @typescript-eslint/no-unsafe-assignment */
272
255
  source={item.source}
273
256
  contentFit={'cover'}
274
257
  contentPosition={'center'}
275
- style={imageStyle}
258
+ style={[styles.image, imageStyle]}
276
259
  />
277
260
  </Pressable>
278
261
  );
279
262
  },
280
- [
281
- currentItem,
282
- imageAspectRatio,
283
- imageHeight,
284
- imageStyle,
285
- imageWidth,
286
- onItemPress,
287
- sharedTransitionTag,
288
- ]
263
+ [currentItem, imageStyle, onItemPress, sharedTransitionTag, styles.image]
289
264
  );
290
265
 
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
- }, []);
266
+ const keyExtractor = useCallback((item: SimpleImageSliderItem) => item.key, []);
307
267
 
308
- const onScaleReset = useCallback(() => {
309
- setScrollEnabled(true);
268
+ const handleListLayout = useCallback((event: LayoutChangeEvent) => {
269
+ if (Platform.OS === 'ios') {
270
+ // temporary workaround for iOS scrolling a bit more than expected
271
+ setSnapToInterval(event.nativeEvent.layout.width - 0.5);
272
+ }
310
273
  }, []);
311
274
 
312
- const estimatedItemSize = useMemo(() => {
313
- return imageWidth ?? (imageHeight ? imageHeight * (imageAspectRatio ?? 4 / 3) : 350);
314
- }, [imageAspectRatio, imageHeight, imageWidth]);
275
+ useEffect(() => {
276
+ setCurrentItem(indexOverride ?? 0);
315
277
 
316
- const internalOnPinchToZoomStatusChange = useCallback(
317
- (status: PinchToZoomStatus) => {
318
- listRef.current?.recordInteraction();
319
- onPinchToZoomStatusChange?.(status);
320
- },
321
- [onPinchToZoomStatusChange]
322
- );
278
+ listRef.current?.scrollToIndex({ index: indexOverride ?? 0, animated: false });
279
+ }, [indexOverride, slicedData]);
323
280
 
324
281
  const list = (
325
282
  <FlashList
@@ -328,12 +285,13 @@ const BaseSimpleImageSlider = forwardRef<
328
285
  disableScrollViewPanResponder={enablePinchToZoom ? !scrollEnabled : false}
329
286
  ref={mergeRefs(ref, listRef)}
330
287
  initialScrollIndex={indexOverride ?? currentItem ?? 0}
331
- onViewableItemsChanged={onViewableItemsChanged}
288
+ onViewableItemsChanged={handleViewableItemsChanged}
332
289
  viewabilityConfig={{
333
290
  itemVisiblePercentThreshold: 55,
334
291
  }}
335
292
  decelerationRate={'fast'}
336
- pagingEnabled={true}
293
+ pagingEnabled={snapToInterval === undefined}
294
+ snapToInterval={snapToInterval}
337
295
  showsHorizontalScrollIndicator={false}
338
296
  showsVerticalScrollIndicator={false}
339
297
  horizontal={true}
@@ -345,22 +303,24 @@ const BaseSimpleImageSlider = forwardRef<
345
303
  width: estimatedItemSize,
346
304
  height: imageHeight ?? estimatedItemSize / imageAspectRatio,
347
305
  }}
306
+ onLayout={handleListLayout}
348
307
  />
349
308
  );
350
309
 
351
310
  return (
352
- <StyledContainer aspectRatio={imageAspectRatio} style={style}>
311
+ <GestureHandlerRootView style={[styles.container, style]}>
353
312
  {enablePinchToZoom ? (
354
- <StyledPinchToZoom
313
+ <PinchToZoom
314
+ style={styles.pinchToZoom}
355
315
  onDismiss={onPinchToZoomRequestClose}
356
- onStatusChange={internalOnPinchToZoomStatusChange}
357
- onScaleChange={onScaleChange}
358
- onScaleReset={onScaleReset}
316
+ onStatusChange={handlePinchToZoomStatusChange}
317
+ onScaleChange={handleScaleChange}
318
+ onScaleReset={handleScaleReset}
359
319
  maximumZoomScale={5}
360
320
  minimumZoomScale={1}
361
321
  >
362
322
  {list}
363
- </StyledPinchToZoom>
323
+ </PinchToZoom>
364
324
  ) : (
365
325
  list
366
326
  )}
@@ -368,54 +328,63 @@ const BaseSimpleImageSlider = forwardRef<
368
328
  renderPageCounter ? (
369
329
  renderPageCounter(currentItem + 1, slicedData.length)
370
330
  ) : (
371
- <ActualPageCounterComponent
372
- position={pageCounterPosition}
373
- totalPages={slicedData.length}
374
- currentPage={currentItem + 1}
375
- style={pageCounterStyle}
376
- textStyle={pageCounterTextStyle}
377
- />
331
+ <AbsoluteComponentContainer position={pageCounterPosition}>
332
+ <PageCounterComponent
333
+ totalPages={slicedData.length}
334
+ currentPage={currentItem + 1}
335
+ style={pageCounterStyle}
336
+ textStyle={pageCounterTextStyle}
337
+ />
338
+ </AbsoluteComponentContainer>
378
339
  )
379
340
  ) : null}
380
341
  {TopRightComponent ? (
381
- <StyledAbsoluteComponentContainer position={'top-right'}>
342
+ <AbsoluteComponentContainer position={'top-right'}>
382
343
  {renderProp(TopRightComponent)}
383
- </StyledAbsoluteComponentContainer>
344
+ </AbsoluteComponentContainer>
384
345
  ) : null}
385
346
  {TopLeftComponent ? (
386
- <StyledAbsoluteComponentContainer position={'top-left'}>
347
+ <AbsoluteComponentContainer position={'top-left'}>
387
348
  {renderProp(TopLeftComponent)}
388
- </StyledAbsoluteComponentContainer>
349
+ </AbsoluteComponentContainer>
389
350
  ) : null}
390
351
  {BottomRightComponent ? (
391
- <StyledAbsoluteComponentContainer position={'bottom-right'}>
352
+ <AbsoluteComponentContainer position={'bottom-right'}>
392
353
  {renderProp(BottomRightComponent)}
393
- </StyledAbsoluteComponentContainer>
354
+ </AbsoluteComponentContainer>
394
355
  ) : null}
395
356
  {BottomLeftComponent ? (
396
- <StyledAbsoluteComponentContainer position={'bottom-left'}>
357
+ <AbsoluteComponentContainer position={'bottom-left'}>
397
358
  {renderProp(BottomLeftComponent)}
398
- </StyledAbsoluteComponentContainer>
359
+ </AbsoluteComponentContainer>
399
360
  ) : null}
400
- </StyledContainer>
361
+ </GestureHandlerRootView>
401
362
  );
402
363
  });
403
364
 
404
- const makeStyledPageCounter = (
405
- PageCounterComponent: React.FunctionComponent<PageCounterProps>
406
- ) => styled(PageCounterComponent)<{
407
- position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
408
- }>`
409
- z-index: 1000;
410
- position: absolute;
411
- bottom: ${({ position }) =>
412
- position === 'bottom-left' || position === 'bottom-right' ? `16px` : 'auto'};
413
- top: ${({ position }) =>
414
- position === 'top-left' || position === 'top-right' ? `16px` : 'auto'};
415
- left: ${({ position }) =>
416
- position === 'top-left' || position === 'bottom-left' ? `16px` : 'auto'};
417
- right: ${({ position }) =>
418
- position === 'top-right' || position === 'bottom-right' ? `16px` : 'auto'};
419
- `;
365
+ const makeStyles = ({
366
+ imageAspectRatio,
367
+ imageWidth,
368
+ imageHeight,
369
+ }: {
370
+ imageAspectRatio?: number;
371
+ imageWidth?: number;
372
+ imageHeight?: number;
373
+ }) => {
374
+ return StyleSheet.create({
375
+ container: {
376
+ width: '100%',
377
+ aspectRatio: imageAspectRatio ?? 4 / 3,
378
+ },
379
+ pinchToZoom: {
380
+ zIndex: 1000,
381
+ },
382
+ image: {
383
+ width: imageWidth ?? '100%',
384
+ height: imageHeight ?? '100%',
385
+ aspectRatio: imageAspectRatio ?? 4 / 3,
386
+ },
387
+ });
388
+ };
420
389
 
421
390
  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,31 +159,42 @@ const FullScreenImageSlider = forwardRef<
168
159
  />
169
160
 
170
161
  {renderDescription && data[internalIndex] ? (
171
- <StyledDescriptionContainer style={styles.descriptionContainer}>
172
- {renderDescription(
173
- data[internalIndex] as SimpleImageSliderItem,
174
- internalIndex
175
- )}
176
- </StyledDescriptionContainer>
162
+ <View style={styles.descriptionContainer}>
163
+ {renderDescription(data[internalIndex], internalIndex)}
164
+ </View>
177
165
  ) : null}
178
- </StyledModalContentContainer>
166
+ </Animated.View>
179
167
  </Modal>
180
168
  );
181
169
  });
182
170
 
183
171
  export default FullScreenImageSlider;
184
172
 
185
- const makeStyles = (safeAreaInsets: EdgeInsets, windowDimensions: ScaledSize) => {
173
+ const makeStyles = (
174
+ safeAreaInsets: EdgeInsets,
175
+ windowDimensions: ScaledSize,
176
+ theme: SimpleImageSliderTheme
177
+ ) => {
186
178
  return StyleSheet.create({
187
179
  closeButton: {
180
+ position: 'absolute',
181
+ zIndex: 1000,
188
182
  top: safeAreaInsets.top,
189
183
  right: safeAreaInsets.right + 20,
190
184
  },
191
185
  modalContent: {
186
+ alignItems: 'center',
187
+ justifyContent: 'center',
188
+ gap: 16,
192
189
  height: windowDimensions.height,
193
190
  width: windowDimensions.width,
194
191
  },
195
192
  descriptionContainer: {
193
+ position: 'absolute',
194
+ borderTopWidth: 1,
195
+ borderTopColor: theme.colors.descriptionContainerBorder,
196
+ width: '100%',
197
+ paddingTop: 20,
196
198
  bottom: safeAreaInsets.bottom + 100,
197
199
  paddingLeft: safeAreaInsets.left + 20,
198
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
  },