Skip to content

Commit

Permalink
Merge pull request #2690 from gluestack/fix/imageviewer-types
Browse files Browse the repository at this point in the history
Fix/imageviewer types
  • Loading branch information
gluestackadmin authored Jan 9, 2025
2 parents 382a268 + 0faec4c commit 24df7df
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import {
import { Icon, CloseIcon } from '@/components/ui/icon';

const ImageViewerBasic = ({ ...props }: any) => {
const Images = [{ id: 1, url: 'https://picsum.photos/1000/1000' }];
const Images = [
{ id: 1, url: 'https://picsum.photos/1000/1000', title: 'Image 1' },
{ id: 2, url: 'https://picsum.photos/1000/1000', title: 'Image 2' },
{ id: 3, url: 'https://picsum.photos/1000/1000', title: 'Image 3' },
];
const [visible, setVisible] = useState(false);
return (
<>
Expand All @@ -29,14 +33,14 @@ const ImageViewerBasic = ({ ...props }: any) => {
>
<ImageViewerBackdrop>
<ImageViewerContent
//@ts-ignore
images={Images}
renderImages={(item: any) => (
<ImageViewerImage key={item.id} source={{ uri: item.url }} />
renderImages={({ item }) => (
<ImageViewerImage source={{ uri: item.url }} />
)}
keyExtractor={(item, index) => item.id + '-' + index}
>
<ImageViewerCloseButton>
<Icon as={CloseIcon}/>
<Icon as={CloseIcon} />
</ImageViewerCloseButton>
</ImageViewerContent>
</ImageViewerBackdrop>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
Icon,
Center,
} from '../../core-components/nativewind';
import {
import {
Pressable,
Image
} from 'react-native';
Expand Down Expand Up @@ -60,23 +60,29 @@ This is an illustration of **ImageViewer** component.
const Images = [{ id: 1, url: 'https://picsum.photos/1000/1000' }];
return (
<Center>
<Pressable onPress={() => setVisible(true)}>
<Image
source={{ uri: Images[0].url }}
className="w-[200px] h-[200px]"
resizeMode="contain"
/>
</Pressable>
<ImageViewer isOpen={visible} onClose={() => setVisible(false)}>
<ImageViewerBackdrop>
<ImageViewerContent images={Images} renderImages={(item) => (<ImageViewerImage key={item.id} source={{ uri: item.url }} />)} >
<ImageViewerCloseButton>
<Icon as={CloseIcon}/>
</ImageViewerCloseButton>
</ImageViewerContent>
</ImageViewerBackdrop>
</ImageViewer>
</Center>
<Pressable onPress={() => setVisible(true)}>
<Image
source={{ uri: Images[0].url }}
className="w-[200px] h-[200px]"
resizeMode="contain"
/>
</Pressable>
<ImageViewer isOpen={visible} onClose={() => setVisible(false)}>
<ImageViewerBackdrop>
<ImageViewerContent
images={Images}
renderImages={(item) => (
<ImageViewerImage key={item.id} source={{ uri: item.url }} />
)}
keyExtractor={(item, index) => item.id + '-' + index}
>
<ImageViewerCloseButton>
<Icon as={CloseIcon}/>
</ImageViewerCloseButton>
</ImageViewerContent>
</ImageViewerBackdrop>
</ImageViewer>
</Center>
);
}
`,
Expand Down Expand Up @@ -306,6 +312,27 @@ The `ImageViewerContent` component is responsible for rendering the images withi
<Table.TText>Yes</Table.TText>
</Table.TD>
</Table.TR>
<Table.TR>
<Table.TD>
<Table.TText>
<InlineCode>keyExtractor</InlineCode>
</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>
<InlineCode>(item: any, index: number) => string</InlineCode>
</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>-</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>Function to extract the key for each image item</Table.TText>
</Table.TD>
<Table.TD>
<Table.TText>Yes</Table.TText>
</Table.TD>
</Table.TR>
</Table.TBody>
</Table>
</TableContainer>
Expand Down Expand Up @@ -350,23 +377,29 @@ import { Center } from '@/components/ui/center';
const Images = [{ id: 1, url: 'https://picsum.photos/1000/1000' }];
return (
<Center>
<Pressable onPress={() => setVisible(true)}>
<Image
source={{ uri: Images[0].url }}
className="w-[200px] h-[200px]"
resizeMode="contain"
/>
</Pressable>
<ImageViewer isOpen={visible} onClose={() => setVisible(false)}>
<ImageViewerBackdrop>
<ImageViewerContent images={Images} renderImages={(item) => (<ImageViewerImage key={item.id} source={{ uri: item.url }} />)} >
<ImageViewerCloseButton>
<Icon as={CloseIcon}/>
</ImageViewerCloseButton>
</ImageViewerContent>
</ImageViewerBackdrop>
</ImageViewer>
</Center>
<Pressable onPress={() => setVisible(true)}>
<Image
source={{ uri: Images[0].url }}
className="w-[200px] h-[200px]"
resizeMode="contain"
/>
</Pressable>
<ImageViewer isOpen={visible} onClose={() => setVisible(false)}>
<ImageViewerBackdrop>
<ImageViewerContent
images={Images}
renderImages={(item) => (
<ImageViewerImage key={item.id} source={{ uri: item.url }} />
)}
keyExtractor={(item, index) => item.id + '-' + index}
>
<ImageViewerCloseButton>
<Icon as={CloseIcon}/>
</ImageViewerCloseButton>
</ImageViewerContent>
</ImageViewerBackdrop>
</ImageViewer>
</Center>
);
}
`,
Expand Down
10 changes: 6 additions & 4 deletions packages/unstyled/image-viewer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export default () => (
renderImages={(item) => (
<ImageViewerImage key={item.id} source={{ uri: item.url }} />
)}
keyExtractor={(item, index) => `${item.id}-${index}`}
/>
</ImageViewerBackdrop>
</ImageViewer>
Expand All @@ -88,10 +89,11 @@ export default () => (

### ImageViewerContent

| Prop | Type | Default | Description |
| ------------ | ------------------------ | ------- | ---------------------------------- |
| images | Array<any> | - | Array of image objects to display |
| renderImages | (item: any) => ReactNode | - | Function to render each image item |
| Prop | Type | Default | Description |
| ------------ | ------------------------------------ | ------- | ----------------------------------------------- |
| images | Array<any> | - | Array of image objects to display |
| renderImages | (item: any) => ReactNode | - | Function to render each image item |
| keyExtractor | (item: any, index: number) => string | - | Function to extract the key for each image item |

More guides on how to get started are available [here](https://ui.gluestack.io/docs/components/media-and-icons/image-viewer).

Expand Down
4 changes: 2 additions & 2 deletions packages/unstyled/image-viewer/src/ImageViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { forwardRef } from 'react';
import { ImageViewerContext } from './ImageViewerContext';
import type { ImageViewerProps } from './types';
import type { InterfaceImageViewerProps } from './types';

const ImageViewer = (StyledRoot: any) =>
forwardRef(
Expand All @@ -10,7 +10,7 @@ const ImageViewer = (StyledRoot: any) =>
isOpen,
onClose,
...props
}: ImageViewerProps & { children: React.ReactNode },
}: InterfaceImageViewerProps & { children: React.ReactNode },
ref?: any
) => {
const [scale, setScale] = React.useState(1);
Expand Down
20 changes: 15 additions & 5 deletions packages/unstyled/image-viewer/src/ImageViewerContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
withTiming,
} from 'react-native-reanimated';
import { Dimensions, StatusBar } from 'react-native';
import type { ImageViewerContentProps } from './types';
import type { InterfaceImageViewerContentProps } from './types';

const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');
const DOUBLE_TAP_DELAY = 300;
Expand All @@ -24,8 +24,9 @@ const ImageViewerContent = (
{
images,
renderImages,
keyExtractor,
children,
}: ImageViewerContentProps & { children: React.ReactNode },
}: InterfaceImageViewerContentProps & { children: React.ReactNode },
ref?: any
) => {
const { onClose, setScale }: any = useContext(ImageViewerContext);
Expand All @@ -45,7 +46,7 @@ const ImageViewerContent = (
.onUpdate((event: any) => {
// Apply the new scale based on the saved scale value
const newScale = savedScale.value * event.scale;
scale.value = Math.min(Math.max(newScale, 0.5), 3);
scale.value = Math.min(Math.max(newScale, 0.5), 10);
focalX.value = event.focalX;
focalY.value = event.focalY;
})
Expand Down Expand Up @@ -147,7 +148,7 @@ const ImageViewerContent = (
);

const animatedStyle = useAnimatedStyle(() => {
setScale(scale.value);
runOnJS(setScale)(scale.value);
if (scale.value <= 1) {
}
return {
Expand All @@ -165,7 +166,16 @@ const ImageViewerContent = (
{children}
<StyledGestureDetector gesture={composedGesture}>
<StyledAnimated style={animatedStyle}>
{images.map(renderImages)}
{images.slice(0, 1).map((item: any, index: number) => {
const RenderImage = renderImages;
return (
<RenderImage
key={keyExtractor ? keyExtractor(item, index) : index}
item={item}
index={index}
/>
);
})}
</StyledAnimated>
</StyledGestureDetector>
</StyledGestureHandlerRootView>
Expand Down
47 changes: 37 additions & 10 deletions packages/unstyled/image-viewer/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,52 @@
export interface ImageViewerContext {
/**
* Callback invoked when the ImageViewer is closed.
*/
onClose: () => void;
/**
* If true, the ImageViewer will open. Useful for controllable state behavior.
*/
isOpen: boolean | undefined;
/**
* The current scale of the Image.
*/
scale: number | undefined;
/**
* Callback function to set the scale of the Image to be used in backdrop for adjusting the opacity.
*/
setScale: (scale: number) => void;
}

export interface ImageViewerProps {
export interface InterfaceImageViewerProps {
/**
* If true, the modal will open. Useful for controllable state behavior.
* If true, the ImageViewer will open. Useful for controllable state behavior.
*/
isOpen?: boolean;
isOpen: boolean;
/**
* Callback invoked when the modal is closed.
* Callback invoked when the ImageViewer is closed.
*/
onClose?: any;
/**
* If true, the modal will be opened by default.
*/
}

export interface ImageViewerContentProps {
images: any;
renderImages: (item: any) => React.ReactNode;
export interface InterfaceImageViewerContentProps {
/**
* The images to display in the ImageViewer.
*/
images: Array<any>;
/**
* Callback React.ReactNode function to render the images.
*/
renderImages: ({
item,
index,
}: {
item: any;
index: number;
}) => React.ReactNode;
/**
* Callback function to extract the key for the images.
*/
keyExtractor: (item: any, index: number) => React.Attributes['key'];
}

export type IImageViewerComponentType<
Expand All @@ -31,10 +56,12 @@ export type IImageViewerComponentType<
ImageViewerBackdropProps
> = React.ForwardRefExoticComponent<
React.PropsWithoutRef<ImageViewerProps> &
InterfaceImageViewerProps &
React.RefAttributes<ImageViewerProps>
> & {
Content: React.ForwardRefExoticComponent<
React.PropsWithoutRef<ImageViewerContentProps> &
InterfaceImageViewerContentProps &
React.RefAttributes<ImageViewerContentProps>
>;
CloseButton: React.ForwardRefExoticComponent<
Expand Down

0 comments on commit 24df7df

Please sign in to comment.