@one-am/react-native-simple-image-slider 0.15.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 (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 -132
  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 -133
  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 +1 -1
  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 +118 -150
  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
  /**
@@ -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
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,78 +245,53 @@ 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
326
- // @ts-expect-error - there's just a small inconsistency with hitSlop typing
327
283
  renderScrollComponent={ScrollView}
328
284
  scrollEnabled={scrollEnabled}
329
285
  disableScrollViewPanResponder={enablePinchToZoom ? !scrollEnabled : false}
330
286
  ref={mergeRefs(ref, listRef)}
331
287
  initialScrollIndex={indexOverride ?? currentItem ?? 0}
332
- onViewableItemsChanged={onViewableItemsChanged}
288
+ onViewableItemsChanged={handleViewableItemsChanged}
333
289
  viewabilityConfig={{
334
290
  itemVisiblePercentThreshold: 55,
335
291
  }}
336
292
  decelerationRate={'fast'}
337
- pagingEnabled={true}
293
+ pagingEnabled={snapToInterval === undefined}
294
+ snapToInterval={snapToInterval}
338
295
  showsHorizontalScrollIndicator={false}
339
296
  showsVerticalScrollIndicator={false}
340
297
  horizontal={true}
@@ -346,22 +303,24 @@ const BaseSimpleImageSlider = forwardRef<
346
303
  width: estimatedItemSize,
347
304
  height: imageHeight ?? estimatedItemSize / imageAspectRatio,
348
305
  }}
306
+ onLayout={handleListLayout}
349
307
  />
350
308
  );
351
309
 
352
310
  return (
353
- <StyledContainer aspectRatio={imageAspectRatio} style={style}>
311
+ <GestureHandlerRootView style={[styles.container, style]}>
354
312
  {enablePinchToZoom ? (
355
- <StyledPinchToZoom
313
+ <PinchToZoom
314
+ style={styles.pinchToZoom}
356
315
  onDismiss={onPinchToZoomRequestClose}
357
- onStatusChange={internalOnPinchToZoomStatusChange}
358
- onScaleChange={onScaleChange}
359
- onScaleReset={onScaleReset}
316
+ onStatusChange={handlePinchToZoomStatusChange}
317
+ onScaleChange={handleScaleChange}
318
+ onScaleReset={handleScaleReset}
360
319
  maximumZoomScale={5}
361
320
  minimumZoomScale={1}
362
321
  >
363
322
  {list}
364
- </StyledPinchToZoom>
323
+ </PinchToZoom>
365
324
  ) : (
366
325
  list
367
326
  )}
@@ -369,54 +328,63 @@ const BaseSimpleImageSlider = forwardRef<
369
328
  renderPageCounter ? (
370
329
  renderPageCounter(currentItem + 1, slicedData.length)
371
330
  ) : (
372
- <ActualPageCounterComponent
373
- position={pageCounterPosition}
374
- totalPages={slicedData.length}
375
- currentPage={currentItem + 1}
376
- style={pageCounterStyle}
377
- textStyle={pageCounterTextStyle}
378
- />
331
+ <AbsoluteComponentContainer position={pageCounterPosition}>
332
+ <PageCounterComponent
333
+ totalPages={slicedData.length}
334
+ currentPage={currentItem + 1}
335
+ style={pageCounterStyle}
336
+ textStyle={pageCounterTextStyle}
337
+ />
338
+ </AbsoluteComponentContainer>
379
339
  )
380
340
  ) : null}
381
341
  {TopRightComponent ? (
382
- <StyledAbsoluteComponentContainer position={'top-right'}>
342
+ <AbsoluteComponentContainer position={'top-right'}>
383
343
  {renderProp(TopRightComponent)}
384
- </StyledAbsoluteComponentContainer>
344
+ </AbsoluteComponentContainer>
385
345
  ) : null}
386
346
  {TopLeftComponent ? (
387
- <StyledAbsoluteComponentContainer position={'top-left'}>
347
+ <AbsoluteComponentContainer position={'top-left'}>
388
348
  {renderProp(TopLeftComponent)}
389
- </StyledAbsoluteComponentContainer>
349
+ </AbsoluteComponentContainer>
390
350
  ) : null}
391
351
  {BottomRightComponent ? (
392
- <StyledAbsoluteComponentContainer position={'bottom-right'}>
352
+ <AbsoluteComponentContainer position={'bottom-right'}>
393
353
  {renderProp(BottomRightComponent)}
394
- </StyledAbsoluteComponentContainer>
354
+ </AbsoluteComponentContainer>
395
355
  ) : null}
396
356
  {BottomLeftComponent ? (
397
- <StyledAbsoluteComponentContainer position={'bottom-left'}>
357
+ <AbsoluteComponentContainer position={'bottom-left'}>
398
358
  {renderProp(BottomLeftComponent)}
399
- </StyledAbsoluteComponentContainer>
359
+ </AbsoluteComponentContainer>
400
360
  ) : null}
401
- </StyledContainer>
361
+ </GestureHandlerRootView>
402
362
  );
403
363
  });
404
364
 
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
- `;
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
+ };
421
389
 
422
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,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
  },