-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: release @animatereactnative/accordion
- Loading branch information
1 parent
9fdaae4
commit 90a4e35
Showing
6 changed files
with
15,038 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,33 +1,125 @@ | ||
# @animatereactnative/accordion | ||
<div align="center"> | ||
<h1>React Native Accordion</h1> | ||
|
||
A React Native Accordion component, powered by React Native Reanimated layout animation that's capable of rendering and animating content that has a dynamic height. | ||
https://github.com/user-attachments/assets/3906847e-c609-4f85-b5e5-b2bb68cef901 | ||
|
||
[![NPM Version](https://img.shields.io/npm/v/@animatereactnative/accordion.svg?style=flat&color=black)](https://www.npmjs.org/package/@animatereactnative/accordion) [![runs with expo](https://img.shields.io/badge/Runs%20with%20Expo-4630EB.svg?style=flat-square&logo=EXPO&labelColor=f3f3f3&logoColor=000)](https://expo.io/) [![npm](https://img.shields.io/npm/l/@animatereactnative/accordion?style=flat-square)](https://www.npmjs.com/package/@animatereactnative/accordion) [![npm](https://img.shields.io/badge/types-included-blue?style=flat-square)](https://www.npmjs.com/package/@animatereactnative/accordion) <a href="https://twitter.com/mironcatalin"><img src="https://img.shields.io/twitter/follow/mironcatalin?label=Follow @mironcatalin&color=black" alt="Follow Miron Catalin"></a> | ||
|
||
</div> | ||
|
||
React Native Accordion component, a cross-platform accordion component, powered by Reanimated, that's capable of displaying dynamic height content and animate the layout changes/transitions between Collapsable and Expandable states. | ||
|
||
If you are going to use this component along with other components (as siblings), it is recommended to use `Accordion.Sibling` and wrap the Siblings with it. This is because the exposed `Sibling` component will use Layout animations as well so there are no layout shifts or sudden movements, keeping everything smooth. | ||
|
||
- 🔋 Powered by Reanimated | ||
- 📱 Works with Expo | ||
- ✅ Cross-platform (iOS, Android, Web - wip) | ||
- ⚡️ 60-120fps | ||
- 🪝 Works with any React Native element/component | ||
- 🎼 Composition ready | ||
- ⌨️ Written in TypeScript | ||
|
||
## Installation | ||
|
||
```sh | ||
npm install @animatereactnative/accordion | ||
``` | ||
|
||
## Usage | ||
> Also, you need to install [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated), and follow their installation instructions. | ||
## Usage | ||
|
||
```js | ||
import { multiply } from '@animatereactnative/accordion'; | ||
import { Accordion } from '@animatereactnative/accordion'; | ||
|
||
// ... | ||
|
||
const result = await multiply(3, 7); | ||
export function Example() { | ||
return ( | ||
<Accordion.Accordion> | ||
<Accordion.Header> | ||
<Text>AnimateReactNative.com</Text> | ||
<Accordion.HeaderIcon> | ||
<ChevronUp /> | ||
</Accordion.HeaderIcon> | ||
</Accordion.Header> | ||
|
||
<Accordion.Collapsed> | ||
<Text>Visible !expanded</Text> | ||
</Accordion.Collapsed> | ||
<Accordion.Always> | ||
<Text>Always visible</Text> | ||
</Accordion.Always> | ||
|
||
<Accordion.Expanded> | ||
<Text>Expanded content</Text> | ||
{loading && <ActivityIndicator />} | ||
{data & <MyList data={data} />} | ||
</Accordion.Expanded> | ||
</Accordion.Accordion> | ||
); | ||
} | ||
``` | ||
|
||
## Props | ||
|
||
``` | ||
Accordion = { | ||
/** | ||
* The main component that will handle the state of the accordion. | ||
* | ||
* @param isOpen boolean | ||
* @param onChange (value: boolean) => void | ||
*/ | ||
Accordion | ||
/** | ||
* The header of the accordion. | ||
*/ | ||
Header, | ||
/** | ||
* The component that will wrap any children and it will apply a rotation to it. | ||
* | ||
* @param children | ||
* @param rotation clockwise | counter-clockwise | ||
*/ | ||
HeaderIcon | ||
/** | ||
* This is the content that will be displayed when the accordion is open | ||
*/ | ||
Expanded, | ||
/** | ||
* This is the content that will be displayed when the accordion is closed | ||
*/ | ||
Collapsed, | ||
/** | ||
* This is the content that will always be displayed | ||
*/ | ||
Always, | ||
/** | ||
* | ||
* This is a component that will add the layout transition to any | ||
* sibling components. Useful when you are rendering other components | ||
* that are not direct children of the Accordion component. | ||
*/ | ||
Sibling, | ||
}; | ||
``` | ||
|
||
## Contributing | ||
|
||
See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow. | ||
|
||
## License | ||
|
||
MIT | ||
[MIT](./LICENSE) | ||
|
||
--- | ||
|
||
Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob) | ||
<p align="center"> | ||
<a href="https://www.animatereactnative.com"> | ||
<picture> | ||
<source media="(prefers-color-scheme: dark)" srcset="https://www.animatereactnative.com/animatereactnative_dark.svg"> | ||
<img alt="AnimateReactNative.com - Premium and Custom React Native animations." src="https://www.animatereactnative.com/animatereactnative_logo.svg" width="50%"> | ||
</picture> | ||
</a> | ||
</p> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,197 @@ | ||
import { useState, useEffect } from 'react'; | ||
import { StyleSheet, View, Text } from 'react-native'; | ||
import { multiply } from '@animatereactnative/accordion'; | ||
import { Accordion } from '@animatereactnative/accordion'; | ||
import { | ||
QueryClient, | ||
QueryClientProvider, | ||
useQuery, | ||
} from '@tanstack/react-query'; | ||
import { ChevronUp } from 'lucide-react-native'; | ||
import { useEffect, useState } from 'react'; | ||
import { | ||
ActivityIndicator, | ||
Image, | ||
ScrollView, | ||
StatusBar, | ||
Text, | ||
View, | ||
} from 'react-native'; | ||
import Animated, { FadeInDown } from 'react-native-reanimated'; | ||
|
||
export default function App() { | ||
const [result, setResult] = useState<number | undefined>(); | ||
export const queryClient = new QueryClient(); | ||
const _spacing = 20; | ||
|
||
useEffect(() => { | ||
multiply(3, 7).then(setResult); | ||
}, []); | ||
async function wait(ms: number) { | ||
return new Promise((resolve) => { | ||
setTimeout(resolve, ms); | ||
}); | ||
} | ||
|
||
const usage = `<Accordion.Accordion> | ||
<Accordion.Header> | ||
<Text>Accordion header</Text> | ||
<Accordion.HeaderIcon> | ||
<ChevronUp /> | ||
</Accordion.HeaderIcon> | ||
</Accordion.Header> | ||
<Accordion.Collapsed> | ||
<Text>Visible !expanded</Text> | ||
</Accordion.Collapsed> | ||
<Accordion.Expanded> | ||
<Text>Collapsed content</Text> | ||
{loading && <ActivityIndicator />} | ||
{data & <MyList data={data}/>} | ||
</Accordion.Expanded> | ||
</Accordion.Accordion>`; | ||
|
||
type Quote = { | ||
id: number; | ||
quote: string; | ||
author: string; | ||
}; | ||
|
||
export default function AccordionExample() { | ||
return ( | ||
<View style={styles.container}> | ||
<Text>Result: {result}</Text> | ||
</View> | ||
<QueryClientProvider client={queryClient}> | ||
<View | ||
style={{ flex: 1, justifyContent: 'center', backgroundColor: '#000' }} | ||
> | ||
<ScrollView contentContainerStyle={{ paddingTop: 100 }}> | ||
<StatusBar hidden /> | ||
<View style={{ gap: _spacing / 2, paddingHorizontal: _spacing }}> | ||
<DummyAccordionWithData type="<Accordion /> component" /> | ||
<DummyAccordionWithData type="Done with Reanimated" /> | ||
<DummyAccordionWithData type="AnimateReactNative.com" /> | ||
<Accordion.Sibling | ||
style={{ | ||
padding: _spacing, | ||
backgroundColor: '#3332', | ||
borderRadius: _spacing, | ||
}} | ||
> | ||
<Text | ||
style={{ | ||
color: '#fff', | ||
opacity: 0.6, | ||
fontSize: 12, | ||
fontFamily: 'Menlo', | ||
}} | ||
> | ||
{usage} | ||
</Text> | ||
<Image | ||
source={{ | ||
uri: 'https://github.com/animate-react-native/.github/blob/main/animatereactnative.com-dark.png?raw=true', | ||
}} | ||
style={{ | ||
width: '70%', | ||
height: 100, | ||
resizeMode: 'contain', | ||
alignSelf: 'center', | ||
}} | ||
/> | ||
</Accordion.Sibling> | ||
</View> | ||
</ScrollView> | ||
</View> | ||
</QueryClientProvider> | ||
); | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
flex: 1, | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
}, | ||
box: { | ||
width: 60, | ||
height: 60, | ||
marginVertical: 20, | ||
}, | ||
}); | ||
function DummyAccordionWithData({ type = 'default' }) { | ||
const [isActive, setIsActive] = useState(false); | ||
const { data, isLoading } = useQuery({ | ||
queryKey: ['dummydata', isActive, type], | ||
enabled: isActive, | ||
queryFn: async () => { | ||
const data = (await fetch( | ||
`https://dummyjson.com/quotes?limit=${ | ||
Math.floor(Math.random() * 4) + 4 | ||
}&skip=${Math.floor(Math.random() * 20)}` | ||
).then((res) => res.json())) as { quotes: Quote[] }; | ||
await wait(1000); | ||
|
||
return data; | ||
}, | ||
}); | ||
|
||
useEffect(() => { | ||
if (!isActive) { | ||
queryClient.resetQueries({ | ||
queryKey: ['dummydata', true, type], | ||
}); | ||
} | ||
}, [isActive, type]); | ||
|
||
return ( | ||
<Accordion.Accordion | ||
onChange={(value) => setIsActive(value)} | ||
style={{ gap: _spacing / 2 }} | ||
> | ||
<Accordion.Header> | ||
<View | ||
style={{ | ||
backgroundColor: 'gold', | ||
padding: _spacing / 2, | ||
borderRadius: _spacing / 2, | ||
flexDirection: 'row', | ||
justifyContent: 'space-between', | ||
alignItems: 'center', | ||
}} | ||
> | ||
<Text style={{ color: '#111', flexShrink: 1 }}>{type}</Text> | ||
<Accordion.HeaderIcon> | ||
<ChevronUp size={20} color="#333" /> | ||
</Accordion.HeaderIcon> | ||
</View> | ||
</Accordion.Header> | ||
<Accordion.Collapsed> | ||
<Text style={{ color: 'white', fontSize: 12 }}> | ||
Hidden when expanded | ||
</Text> | ||
</Accordion.Collapsed> | ||
<Accordion.Always> | ||
<Text style={{ color: 'white', fontSize: 10 }}> | ||
Get more details about {type} | ||
</Text> | ||
</Accordion.Always> | ||
<Accordion.Expanded | ||
style={{ | ||
backgroundColor: '#333', | ||
width: '100%', | ||
borderRadius: _spacing / 2, | ||
padding: _spacing / 2, | ||
}} | ||
> | ||
{isLoading && <ActivityIndicator color={'white'} />} | ||
{!isLoading && data?.quotes && ( | ||
<View style={{ gap: _spacing / 2 }}> | ||
{data?.quotes?.map((quote, index) => ( | ||
<Animated.View | ||
key={quote.id} | ||
entering={FadeInDown.springify() | ||
.damping(80) | ||
.stiffness(200) | ||
.delay(index * 75)} | ||
> | ||
<Text | ||
style={{ | ||
color: '#fff', | ||
opacity: 0.7, | ||
fontSize: 12, | ||
fontFamily: 'Menlo', | ||
}} | ||
> | ||
{quote.quote} | ||
</Text> | ||
<Text style={{ color: '#fff' }}>{quote.author}</Text> | ||
</Animated.View> | ||
))} | ||
</View> | ||
)} | ||
</Accordion.Expanded> | ||
</Accordion.Accordion> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.