Giter Site home page Giter Site logo

r0b0t3d / react-native-collapsible Goto Github PK

View Code? Open in Web Editor NEW
43.0 3.0 5.0 9.79 MB

Fully customizable collapsible views

License: MIT License

JavaScript 2.81% Java 9.32% TypeScript 80.67% C 0.13% Objective-C 3.00% Swift 0.08% Ruby 2.99% Objective-C++ 0.98%
collapsible collapsible-headers collapsible-accordion

react-native-collapsible's Introduction

@r0b0t3d/react-native-collapsible

Fully customizable collapsible views

alt text

Installation

yarn add @r0b0t3d/react-native-collapsible

I am using reanimated 2 for animation. So you should install and follow instruction here to setup your project react-native-reanimated

Key features

1️⃣ Support FlatList/ScrollView 2️⃣ Support sticky header 3️⃣ Can have multiple sticky headers 4️⃣ Easy to customize

Usage

1. Basic

import {
  CollapsibleContainer,
  CollapsibleFlatList,
  CollapsibleScrollView,
} from '@r0b0t3d/react-native-collapsible';

// ...
const MyComponent = () => {
  const {
    collapse,   // <-- Collapse header
    expand,     // <-- Expand header
    scrollY,    // <-- Animated scroll position. In case you need to do some animation in your header or somewhere else
  } = useCollapsibleContext();

  return (
    <CollapsibleContainer>          // 1️⃣ (Required) Outermost container 
      <CollapsibleHeaderContainer>  // 2️⃣ (Required) Header view
        <!-- Your header view -->
        <StickyView>                // 3️⃣ Sticky view
          <!-- Your sticky view goes here -->
        </StickyView>
      </CollapsibleHeaderContainer>
      <CollapsibleFlatList          // 4️⃣ (Required) Your FlatList/ScrollView
        data={data}
        renderItem={renderItem}
        headerSnappable={false} // <-- should header auto snap when you release the finger
      />
    </CollapsibleContainer>
  )
}

export default withCollapsibleContext(MyComponent); // 5️⃣ (Required)wrap your component with `withCollapsibleContext`

2. Advance

We support multiple CollapsibleHeaderContainer and StickyView. It come useful in case you need to break your code into smaller component.

Parent.tsx

const Parent = () => {
  const [tabIndex, setTabIndex] = useState(0)

  return (
    <CollapsibleContainer>
      <CollapsibleHeaderContainer>
        <!-- Your header view -->
        <StickyView>
          <TabView currentTab={tabIndex} onTabChange={setTabIndex} />
        </StickyView>
      </CollapsibleHeaderContainer>

      {tabIndex === 0 && <FirstTab />}
      {tabIndex === 1 && <SecondTab />}
    </CollapsibleContainer>
  )
}

FirstTab.tsx

const FirstTab = () => {
  return (
    <>
      <CollapsibleHeaderContainer>
        <!-- 😍 You can have another headerview in each tab where you can add another StickyView there -->
        <StickyView>
          <!-- Your sticky view goes here -->
        </StickyView>
        <View />
        <StickyView>
          <!-- 🚀 Bonus, multiple sticky view -->
        </StickyView>
      </CollapsibleHeaderContainer>
      <CollapsibleFlatList
        data={data}
        renderItem={renderItem}
      />
    </>
  )
}

Showcases

Note: Feel free to add your showcase here by a PR
App Gif
ANU Global

Breaking changes

1.0.0

  • Removed

    • persistHeaderHeight
    • contentMinHeight
  • Added

    • CollapsibleContainer
    • StickyView

Tips

1. Trigger scroll when scroll inside CollapsibleHeaderContainer

  • If your header doesn't contains touchable component, try pointerEvents="none"
<CollapsibleHeaderContainer>
  <View pointerEvents="none">
    <Text>Header</Text>
  </View>
</CollapsibleHeaderContainer>
  • If your header contains touchable componet, try to set pointerEvents="box-none" for every nested views that contains touchable, otherwise use pointerEvents="none"
<CollapsibleHeaderContainer>
    <View pointerEvents="box-none"> // <-- this is parent view
        <View pointerEvents="none"> // <-- this view doesn't contain touchable component
            <Text>Header</Text>
        </View>
        <View pointerEvents="box-none"> // <-- this view contain touchable component
            <View pointerEvents="none">
                <Text>Some text</Text>
            </View>
            <TouchableOpacity>
                <Text>Button</Text>
            </TouchableOpacity>
        </View>
    </View>
</CollapsibleHeaderContainer>

Contributing

See the contributing guide to learn how to contribute to the repository and the development workflow.

License

MIT

react-native-collapsible's People

Contributors

r0b0t3d avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

react-native-collapsible's Issues

Touchoubles not working in StickyView

When touchable are inserted in StickyView, they work only when scroll is on top. They are always on the top, but they are not working when scroller down. Same with both FlatList and ScrollView. Also used recommended pointerEvents. Any suggestion?

      <CollapsibleHeaderContainer>
        <StickyView pointerEvents="box-none">
          <Button onPress={() => console.log('aaaaaaa')} />
        </StickyView>
        <View height="50px" color="purple" pointerEvents="none" />
        <StickyView pointerEvents="box-none">
          <View color="yellow" pointerEvents="box-none">
            <TextInput />
          </View>
        </StickyView>
      </CollapsibleHeaderContainer>
      <CollapsibleFlatList // 4️⃣ (Required) Your FlatList/ScrollView
        data={Array(10)}
        keyExtractor={(item, index) => index}
        renderItem={({item, index}) => (
          <View height="150px" color={index % 2 === 0 ? 'blue' : 'red'}>
            <Text>saldjaslkd</Text>
          </View>
        )}
        headerSnappable={false}
      />
    </CollapsibleContainer>```

Pull to refresh on CollapsibleFlatList

Hello, first of all, thank you for providing the code for the general to use. I have been using your code in my project and I have encountered a problem. How do you pull the CollapsibleFlatList such that the refresh control gets triggered? Here is how I am using the code:

import {
    View,
    Text,
    StyleSheet,
    Platform,
    SafeAreaView,
    Pressable
} from "react-native"
import { background, borderColor, select, white } from "../common/Colors"
import { StackNavigationProp } from "@react-navigation/stack";
import { RootStackParamList } from "../navigation/StackNavigator";
import { RouteProp, useNavigation, useRoute } from "@react-navigation/native";
import DiscoveryScreen from "./DiscoveryScreen";
import MarqueeContainer from "./MarqueeContainer";
import { useState } from "react";
import ReactNativeHapticFeedback from "react-native-haptic-feedback";
import { hapticOptions } from "../common/Global";
import {
    CollapsibleContainer,
    CollapsibleHeaderContainer,
    CollapsibleScrollView,
    withCollapsibleContext,
    StickyView,
    useCollapsibleContext,
} from "@r0b0t3d/react-native-collapsible";

type DiscoveryPageProps = StackNavigationProp<RootStackParamList, 'DiscoveryPage'>

const DiscoveryPage = () => {

    const navigation = useNavigation<DiscoveryPageProps>()
    const route = useRoute<RouteProp<RootStackParamList>>()

    const [showPublic, setShowPublic] = useState(true)

    return (
        /* @ts-ignore -- type definition for collapsible container is wrong, we should submit PR to author */
        <CollapsibleContainer style={styles.container}>
            <CollapsibleHeaderContainer>
                <View style={styles.titleContainer}>
                    <Text style={styles.title}>
                        DISCOVER
                    </Text>
                </View>

                {/* @ts-ignore -- type definition for collapsible container is wrong, we should submit PR to author */}
                <StickyView>
                    <View style={styles.menuContainer}>
                        <Pressable
                            style={[
                                styles.menu,
                                showPublic ? styles.menuSelected : null
                            ]}
                            onPress={() => {
                                ReactNativeHapticFeedback.trigger("soft", hapticOptions);
                                setShowPublic(true)
                            }}
                        >
                            <Text style={styles.text}>
                                Public
                            </Text>
                        </Pressable>

                        <Pressable
                            style={[
                                styles.menu,
                                !showPublic ? styles.menuSelected : null
                            ]}
                            onPress={() => {
                                ReactNativeHapticFeedback.trigger("soft", hapticOptions);
                                setShowPublic(false)
                            }}
                        >
                            <Text style={styles.text}>
                                Follow
                            </Text>
                        </Pressable>
                    </View>

                    <MarqueeContainer />
                </StickyView>
            </CollapsibleHeaderContainer>

            <DiscoveryScreen
                navigation={navigation}
                route={route}
                showPublic={showPublic} />
        </CollapsibleContainer>
    )
}

const styles = StyleSheet.create({
    container: {
        marginTop: 50,
        flex: 1,
    },
    titleContainer: {
        backgroundColor: background,
        flexDirection: 'row',
        alignItems: 'center',
        justifyContent: 'center',
        width: '100%'
    },
    title: {
        fontSize: 40,
        fontFamily: Platform.OS === 'ios' ?
            'Raleway ExtraBold' : 'Raleway-ExtraBold',
        margin: 10,
    },
    menuContainer: {
        backgroundColor: background,
        flexDirection: 'row',
        justifyContent: 'center',
        paddingBottom: 15,
    },
    menu: {
        marginHorizontal: 10,
        paddingHorizontal: 20,
        paddingVertical: 2,
        borderColor: borderColor,
        borderRadius: 20,
        borderWidth: 1,
        backgroundColor: white
    },
    menuSelected: {
        backgroundColor: select,
    },
    text: {
        fontSize: 18,
        fontFamily: Platform.OS === 'ios' ?
            'Raleway SemiBold' : 'Raleway-SemiBold',
        textTransform: 'uppercase'
    },
    show: {
        display: 'flex',
        flex: 1,
    },
    hidden: {
        display: 'none'
    }
})

export default withCollapsibleContext(DiscoveryPage)
import { useEffect, useState } from "react"
import { StyleSheet, View, FlatList, RefreshControl } from "react-native"
import { WineRecord } from "../../../../common/src/models/WineRecord"
import { background, backgroundDark, borderColor, textPlaceholder, textSecondary } from "../common/Colors"
import DiscoveryItem from "./DiscoveryItem"
import EmptyDisplay from "./EmptyDisplay"
import { usePostListContext } from "../common/DiscoveryState"
import FooterComponent from "./FooterComponent"
import { useFollowingContext } from "../common/UserState"
import { Profile } from "../../../../common/src/models/Profile"
import GetCurrentAuthUid from "../../services/session/GetCurrentAuthUid"
import { useSelectedWine } from "../common/WineState"
import { CollapsibleFlatList } from "@r0b0t3d/react-native-collapsible"

interface DiscoveryScreenProps {
    navigation: any,
    route: any,
    showPublic: boolean
}

const DiscoveryScreen = (props: DiscoveryScreenProps) => {

    const [[__, setSelectedWine], ___] = useSelectedWine()
    const [[postList, ____], _, refreshData, loadData] = usePostListContext()
    const [[following, _____], loadFollowing] = useFollowingContext()

    const [followingPostList, setFollowingPostList] = useState<WineRecord[]>([])

    const [loading, setLoading] = useState<boolean>(false)
    const [refreshing, setRefreshing] = useState<boolean>(false)

    useEffect(() => {
        _filterData()
    }, [following, postList])

    const _renderItem = ({ item }: { item: WineRecord }) => (
        <DiscoveryItem
            key={item.key}
            item={item}
            navigation={props.navigation}
            setSelectedWine={setSelectedWine}
        />
    )

    const _filterPosts = () => {
        const uidSet: Set<string> = new Set(following.map((profile: Profile) => profile.uid))
        const filteredList = postList.filter((post: WineRecord) => uidSet.has(post.userKey))
        setFollowingPostList(filteredList)
    }

    const _filterData = () => {
        if (!following) {
            loadFollowing(GetCurrentAuthUid())
        } else {
            _filterPosts()
        }
    }

    const _refreshData = () => {
        setRefreshing(true)
        refreshData(() => {
            setRefreshing(false)
        })
    }

    const _loadData = async () => {
        setLoading(true)
        loadData(() => {
            setLoading(false)
        })
    }

    return (
        <CollapsibleFlatList
            data={props.showPublic ? postList : followingPostList}
            renderItem={_renderItem}
            onEndReachedThreshold={0.001}
            onEndReached={_loadData}
            refreshControl={<RefreshControl
                refreshing={refreshing}
                onRefresh={_refreshData}
                colors={[textPlaceholder]}
            />}
            ListHeaderComponent={<View style={styles.bar} />}
            ListFooterComponent={<FooterComponent loading={loading} />}
            ListEmptyComponent={<EmptyDisplay loading={loading} />}
            contentContainerStyle={styles.contentContainer}
        />
    )
}

const styles = StyleSheet.create({
    container: {
        width: '100%',
        flex: 1,
        backgroundColor: background
    },
    contentContainer: {
        flexGrow: 1,
        justifyContent: 'flex-start'
    },
    bar: {
        borderColor: borderColor,
        borderTopWidth: 1,
        width: '100%',
        top: -1,
    }
})

export default DiscoveryScreen

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.