react-native-wallet-card-stack
Version:
A customizable React Native component for displaying animated stacks of card groups with smooth scrolling and TypeScript support.
195 lines (158 loc) • 7.72 kB
Markdown
# react-native-wallet-card-stack
A customizable React Native component for displaying animated stacks of card groups. Each group scrolls with smooth animations, and within each group, up to 3 cards are stacked with their own animation effects. Ideal for creating visually engaging card-based UIs like wallets, galleries, or dashboards.

### Check out the `CardStack` component in action:
[Demo Video](https://www.youtube.com/shorts/y9ets6PTXmY)
## Note :
The whole card stack moving animation currently only works smoothly on iOS.
# Features
- **Animated Scrolling**: Card groups animate as you scroll, with the entire stack shifting based on a configurable top offset.
- **Customizable**: Adjust card height, padding, spacing, and more via props.
- **Group Limit**: Automatically limits each group to a maximum of 3 cards.
- **TypeScript Support**: Fully typed for better developer experience.
## Installation
Install the package via npm:
npm install react-native-card-stack
Or with yarn:
yarn add react-native-card-stack
### Dependencies
Ensure you have the following peer dependencies installed:
- react
- react-native
## Usage
Here’s a basic example of how to use CardStack in your React Native app:
import React from "react";
import { SafeAreaView, StyleSheet, Text, View } from "react-native";
import CardStack from "react-native-wallet-card-stack";
const Base = () => {
const [selectedGroup, setSelectedGroup] = React.useState<string | null>(null);
// Handler for card press events
const handleCardPress = (groupIndex: number) => {
setSelectedGroup(testData[groupIndex][0].city);
console.log(
`Pressed group at index: ${groupIndex} - ${testData[groupIndex][0].city}`
);
};
return (
<SafeAreaView style={styles.container}>
<Text style={styles.header}>Card Stack Demo</Text>
{/* Display selected group info */}
<View style={styles.infoContainer}>
{selectedGroup !== null ? (
<Text style={styles.infoText}>Selected Group: {selectedGroup}</Text>
) : (
<Text style={styles.placeholderText}>Tap a card group to select</Text>
)}
</View>
{/* Spacer before the card stack */}
<View style={styles.spacer} />
{/* Card Stack Component */}
<CardStack
data={testData}
renderCard={(item, groupIndex, cardIndex) => (
<View style={{ padding: 10 }}>
<Text>{item.city}</Text>
</View>
)}
cardHeight={400}
cardPadding={50}
cardSpace={10}
topOffset={200}
onCardPress={handleCardPress}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#f5f5f5",
},
header: {
fontSize: 24,
fontWeight: "bold",
padding: 20,
textAlign: "center",
},
infoContainer: {
paddingHorizontal: 20,
paddingVertical: 10,
alignItems: "center",
},
infoText: {
fontSize: 18,
color: "#fff",
backgroundColor: "#4CAF50",
padding: 10,
borderRadius: 8,
},
placeholderText: {
fontSize: 16,
color: "#666",
},
spacer: {
height: 200, // Reduced from 300 for better balance
},
cardContent: {
padding: 15,
backgroundColor: "#fff",
borderRadius: 8,
height: "100%", // Ensure it fills the card height
justifyContent: "center",
},
cardTitle: {
fontSize: 20,
fontWeight: "bold",
marginBottom: 5,
},
cardDescription: {
fontSize: 14,
color: "#333",
},
});
export default Base;
export const testData = [
// New York
[
{ city: 'New York', sold: 75 },
{ city: 'New York', sold: 60 },
],
// Philadelphia
[
{ city: 'Philadelphia', sold: 85 },
{ city: 'Philadelphia', sold: 65 },
],
// Los Angeles
[
{ city: 'Los Angeles', sold: 40 },
{ city: 'Los Angeles', sold: 70 },
],
// Chicago
[
{ city: 'Chicago', sold: 55 },
{ city: 'Chicago', sold: 45 },
],
// Miami
[
{ city: 'Miami', sold: 65 },
{ city: 'Miami', sold: 50 },
],
];
## Props
SmartyPants converts ASCII punctuation characters into "smart" typographic punctuation HTML entities. For example:
## Props
| Prop | ASCII Type | ASCII Default | Description |
| -------------------- | ----------------------------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------------------------------ |
| `data` | `T[][]` | Required | Array of card groups, where each group is an array of card data objects. |
| `renderCard` | `(item: T, groupIndex: number, cardIndex: number) => JSX.Element` | Required | Function to render each card, receiving the item, group index, and card index. |
| `cardHeight` | `number` | `400` | Height of each card in pixels. |
| `cardPadding` | `number` | `57` | Padding between card groups in the animation. |
| `cardSpace` | `number` | `6` | Space between cards within a group in the animation. |
| `topOffset` | `number` | `Dimensions.get('window').height * 0.35` | Top offset for the stack animation trigger. |
| `keyExtractor` | `(item: T[], groupIndex: number) => string` | `(item, index) => ${index}` | Key extractor for card groups. |
| `cardKeyExtractor` | `(item: T, cardIndex: number, groupIndex: number) => string` | `(item, index, groupIndex) => ${groupIndex}-${index}` | Key extractor for cards within a group. |
| `onCardPress` | `(groupIndex: number) => void` | `undefined` | Callback when a card group is pressed, receives the group index. |
| `flatListProps` | `Partial<FlatListProps<T[]>>` | `undefined` | Additional props for the outer FlatList. |
| `innerFlatListProps` | `Partial<FlatListProps<T>>` | `undefined` | Additional props for the inner FlatList (per group). |
```
```