import { useState, useRef, useId } from 'react';
import {
View,
ScrollView,
Image,
StyleSheet,
TouchableOpacity,
} from 'react-native';
import ZenText from './../themed/ZenText';
import { useTheme } from './../../hooks/useTheme';
import type { ThemeType } from '../../literals/Type.literal';
import ColorUtil from '../../util/Color.util';
import ThemeConfig from '../../config/Theme.config';
import LayoutConfig from '../../config/LayoutConfig';
/**
* @docunator
* @title ZenImageSliderItem
* @description Props for each item in ZenImageSlider component.
* @author Danilo Ramírez Mattey
* @category Widget Components Props
* @version 1.0.0
*/
export type ZenImageSliderItem = {
source: any;
onPress?: () => void;
title?: string;
};
type ZenImageSliderItemRenderProps = {
image: ZenImageSliderItem;
styles: any;
};
function ZenImageSliderItemRender({
image,
styles,
}: ZenImageSliderItemRenderProps) {
return (
{image.title && (
{image.title}
)}
);
}
/**
* @docunator
* @title ZenImageSliderProps
* @description Props for ZenImageSlider component.
* @author Danilo Ramírez Mattey
* @category Widget Components Props
* @version 1.0.0
*/
type ZenImageSliderProps = {
type?: ThemeType;
images: ZenImageSliderItem[];
ratio?: number;
pagination?: boolean;
paginationRelationToImage?: number;
bordered?: boolean;
};
/**
* @docunator
* @title ZenImageSlider
* @description A simple image slider component with pagination.
* @author Danilo Ramírez Mattey
* @category Widget Components
* @version 1.0.0
* @param {ZenImageSliderItem[]} images - Array of images to display. Each image can have a source, optional onPress action, and optional title. Format: { source: any; onPress?: () => void; title?: string; }
* @param {string} type - Theme type
* @param {number} ratio - Aspect ratio (width / height)
* @param {boolean} pagination - Show pagination dots
* @param {number} paginationRelationToImage - Size of pagination dots in relation to image width
* @param {boolean} bordered - Show border around the slider
* @example {tsx}
import {
Layout,
Screen,
ZenImageSlider,
type ZenImageSliderItem,
ZenSpaceBlock,
} from 'react-zen-ui';
export default function SliderScreen() {
const imageBase = 'https://picsum.photos/600?';
const images: ZenImageSliderItem[] = [
{source:{uri:imageBase + Math.random().toString()}, title: 'Image with Text'},
{source:{uri:imageBase + Math.random().toString()}},
{source:{uri:imageBase + Math.random().toString()} , title: 'Another Image with Text'},
{source:{uri:imageBase + Math.random().toString()}},
{source:{uri:imageBase + Math.random().toString()}, onPress: ()=>{}, title:'This image supports onPress action'},
];
return (
<>
>
);
}
{/tsx}
@snack @daniloramirezcr/c906b8
*/
export default function ZenImageSlider({
images,
type = 'primary',
ratio = 16 / 9,
pagination = true,
paginationRelationToImage = 35,
bordered = false,
}: ZenImageSliderProps) {
const theme = useTheme();
if (!theme[type]) {
throw new Error('ZenImageSlider: theme type "' + type + '" not found.');
}
const rId = useId();
const [activeIndex, setActiveIndex] = useState(0);
const scrollViewRef = useRef(null);
const [containerWidth, setContainerWidth] = useState(0);
/**
* Handles the scroll event to update the active index.
* @param event
*/
const handleScroll = (event: any) => {
const contentOffsetX = event.nativeEvent.contentOffset.x;
const newIndex = Math.round(contentOffsetX / containerWidth);
setActiveIndex(newIndex);
};
/**
* Handles layout changes to get container width.
* @param event
*/
const handleLayout = (event: any) => {
const { width } = event.nativeEvent.layout;
setContainerWidth(width);
};
/**
* Styles
* A lot of them depend on containerWidth, so they need to be inside the component.
*/
const styles = StyleSheet.create({
container: {
flex: 1,
// maxWidth: '40%'
borderWidth: bordered ? 1 : 0,
borderColor: bordered ? theme[type] : 'transparent',
borderRadius: bordered ? LayoutConfig.border : 0,
overflow: 'hidden',
},
imageContainer: {
width: containerWidth - (bordered ? 2 : 0),
height: 'auto',
},
image: {
width: containerWidth - (bordered ? 2 : 0),
aspectRatio: ratio,
},
textContainer: {
width: containerWidth,
position: 'absolute',
zIndex: 2,
top: LayoutConfig.space / 2,
justifyContent: 'center',
alignItems: 'center',
},
imageTitle: {
flex: 1,
// left: 10,
color: theme.text,
backgroundColor: ColorUtil.hexToRgb(
theme[type],
ThemeConfig.defaultDimValue
),
// left: '50%',
// marginHorizontal: 'auto',
paddingHorizontal: LayoutConfig.space / 2,
paddingVertical: LayoutConfig.space / 4,
borderRadius: LayoutConfig.border / 2,
},
pagination: {
flexDirection: 'row',
position: 'absolute',
bottom: LayoutConfig.space,
alignSelf: 'center',
},
dot: {
width: containerWidth / paginationRelationToImage,
height: containerWidth / paginationRelationToImage,
borderRadius: 999,
marginHorizontal: 4,
},
activeDot: {
backgroundColor: theme[type],
},
inactiveDot: {
backgroundColor: ColorUtil.hexToRgb(
theme.text,
ThemeConfig.defaultDimValue
),
},
});
return (
{images.map((image, index) => (
{image.onPress ? (
) : (
)}
))}
{pagination && (
{images.map((_, index) => (
))}
)}
);
}