├── .github
├── FUNDING.yml
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .npmignore
├── .prettierignore
├── .vscode
└── settings.json
├── Example
├── .expo-shared
│ └── assets.json
├── .gitignore
├── App.tsx
├── app.json
├── assets
│ ├── fonts
│ │ └── SpaceMono-Regular.ttf
│ └── images
│ │ ├── adaptive-icon.png
│ │ ├── favicon.png
│ │ ├── icon.png
│ │ └── splash.png
├── babel.config.js
├── components
│ ├── EditScreenInfo.tsx
│ ├── StyledText.tsx
│ ├── Themed.tsx
│ └── __tests__
│ │ └── StyledText-test.js
├── constants
│ ├── Colors.ts
│ └── Layout.ts
├── hooks
│ ├── useCachedResources.ts
│ └── useColorScheme.ts
├── metro.config.js
├── navigation
│ ├── LinkingConfiguration.ts
│ └── index.tsx
├── package.json
├── screens
│ ├── BasicScreen.tsx
│ ├── HorizontalScreen.tsx
│ ├── NestedScreen.tsx
│ ├── NotFoundScreen.tsx
│ └── SwipeableScreen.tsx
├── tsconfig.json
├── types.tsx
├── utils
│ └── index.ts
└── yarn.lock
├── LICENSE.txt
├── README.md
├── babel.config.js
├── jest-setup.js
├── package.json
├── src
├── components
│ ├── CellDecorators.tsx
│ ├── CellRendererComponent.tsx
│ ├── DraggableFlatList.tsx
│ ├── NestableDraggableFlatList.tsx
│ ├── NestableScrollContainer.tsx
│ ├── PlaceholderItem.tsx
│ ├── RowItem.tsx
│ └── ScrollOffsetListener.tsx
├── constants.ts
├── context
│ ├── animatedValueContext.tsx
│ ├── cellContext.tsx
│ ├── draggableFlatListContext.tsx
│ ├── nestableScrollContainerContext.tsx
│ ├── propsContext.tsx
│ └── refContext.tsx
├── hooks
│ ├── useAutoScroll.tsx
│ ├── useCellTranslate.tsx
│ ├── useNestedAutoScroll.tsx
│ ├── useOnCellActiveAnimation.ts
│ └── useStableCallback.ts
├── index.tsx
├── types.ts
└── utils.ts
├── tests
└── index.test.js
├── tsconfig.json
└── yarn.lock
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [computerjazz]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | *This package has external dependencies of [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated) and [react-native-gesture-handler](https://github.com/software-mansion/react-native-gesture-handler) which must be installed separately. Before opening an issue related to animations or gestures please verify that you have completed ALL installation steps, including the [changes to MainActivity.](https://software-mansion.github.io/react-native-gesture-handler/docs/getting-started.html#android)*
11 |
12 | **Describe the bug**
13 | A clear and concise description of what the bug is.
14 |
15 | **To Reproduce**
16 | Link a snack if possible.
17 |
18 | **Platform & Dependencies**
19 | Please list any applicable dependencies in addition to those below (react-navigation etc).
20 | - react-native-draggable-flatlist version:
21 | - Platform:
22 | - React Native or Expo version:
23 | - Reanimated version:
24 | - React Native Gesture Handler version:
25 |
26 | **Additional context**
27 | Add any other context about the problem here.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | /lib
3 | coverage/
4 | .idea
5 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | /Example
2 | /tests
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Ignore all markdown files:
2 | *.md
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "typescript.tsdk": "node_modules/typescript/lib"
3 | }
4 |
--------------------------------------------------------------------------------
/Example/.expo-shared/assets.json:
--------------------------------------------------------------------------------
1 | {
2 | "e997a5256149a4b76e6bfd6cbf519c5e5a0f1d278a3d8fa1253022b03c90473b": true,
3 | "af683c96e0ffd2cf81287651c9433fa44debc1220ca7cb431fe482747f34a505": true,
4 | "12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
5 | "40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
6 | }
7 |
--------------------------------------------------------------------------------
/Example/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .expo/
3 | dist/
4 | npm-debug.*
5 | *.jks
6 | *.p8
7 | *.p12
8 | *.key
9 | *.mobileprovision
10 | *.orig.*
11 | web-build/
12 |
13 | # macOS
14 | .DS_Store
15 |
--------------------------------------------------------------------------------
/Example/App.tsx:
--------------------------------------------------------------------------------
1 | import { StatusBar } from "expo-status-bar";
2 | import { SafeAreaProvider } from "react-native-safe-area-context";
3 |
4 | import useCachedResources from "./hooks/useCachedResources";
5 | import useColorScheme from "./hooks/useColorScheme";
6 | import Navigation from "./navigation";
7 |
8 | export default function App() {
9 | const isLoadingComplete = useCachedResources();
10 | const colorScheme = useColorScheme();
11 |
12 | if (!isLoadingComplete) {
13 | return null;
14 | } else {
15 | return (
16 |
17 |
18 |
19 |
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "Example",
4 | "slug": "Example",
5 | "version": "1.0.0",
6 | "orientation": "portrait",
7 | "icon": "./assets/images/icon.png",
8 | "scheme": "myapp",
9 | "userInterfaceStyle": "automatic",
10 | "splash": {
11 | "image": "./assets/images/splash.png",
12 | "resizeMode": "contain",
13 | "backgroundColor": "#ffffff"
14 | },
15 | "updates": {
16 | "fallbackToCacheTimeout": 0
17 | },
18 | "assetBundlePatterns": ["**/*"],
19 | "ios": {
20 | "supportsTablet": true
21 | },
22 | "android": {
23 | "adaptiveIcon": {
24 | "foregroundImage": "./assets/images/adaptive-icon.png",
25 | "backgroundColor": "#ffffff"
26 | }
27 | },
28 | "web": {
29 | "favicon": "./assets/images/favicon.png"
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Example/assets/fonts/SpaceMono-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/computerjazz/react-native-draggable-flatlist/5b2f9665a67c639b8a691cf1b8ea717161a7a7f9/Example/assets/fonts/SpaceMono-Regular.ttf
--------------------------------------------------------------------------------
/Example/assets/images/adaptive-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/computerjazz/react-native-draggable-flatlist/5b2f9665a67c639b8a691cf1b8ea717161a7a7f9/Example/assets/images/adaptive-icon.png
--------------------------------------------------------------------------------
/Example/assets/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/computerjazz/react-native-draggable-flatlist/5b2f9665a67c639b8a691cf1b8ea717161a7a7f9/Example/assets/images/favicon.png
--------------------------------------------------------------------------------
/Example/assets/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/computerjazz/react-native-draggable-flatlist/5b2f9665a67c639b8a691cf1b8ea717161a7a7f9/Example/assets/images/icon.png
--------------------------------------------------------------------------------
/Example/assets/images/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/computerjazz/react-native-draggable-flatlist/5b2f9665a67c639b8a691cf1b8ea717161a7a7f9/Example/assets/images/splash.png
--------------------------------------------------------------------------------
/Example/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true);
3 | return {
4 | presets: ["babel-preset-expo"],
5 | plugins: ["react-native-reanimated/plugin"],
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/Example/components/EditScreenInfo.tsx:
--------------------------------------------------------------------------------
1 | import * as WebBrowser from "expo-web-browser";
2 | import { StyleSheet, TouchableOpacity } from "react-native";
3 |
4 | import Colors from "../constants/Colors";
5 | import { MonoText } from "./StyledText";
6 | import { Text, View } from "./Themed";
7 |
8 | export default function EditScreenInfo({ path }: { path: string }) {
9 | return (
10 |
11 |
12 |
17 | Open up the code for this screen:
18 |
19 |
20 |
25 | {path}
26 |
27 |
28 |
33 | Change any of the text, save the file, and your app will automatically
34 | update.
35 |
36 |
37 |
38 |
39 |
40 |
41 | Tap here if your app doesn't automatically update after making
42 | changes
43 |
44 |
45 |
46 |
47 | );
48 | }
49 |
50 | function handleHelpPress() {
51 | WebBrowser.openBrowserAsync(
52 | "https://docs.expo.io/get-started/create-a-new-app/#opening-the-app-on-your-phonetablet"
53 | );
54 | }
55 |
56 | const styles = StyleSheet.create({
57 | getStartedContainer: {
58 | alignItems: "center",
59 | marginHorizontal: 50,
60 | },
61 | homeScreenFilename: {
62 | marginVertical: 7,
63 | },
64 | codeHighlightContainer: {
65 | borderRadius: 3,
66 | paddingHorizontal: 4,
67 | },
68 | getStartedText: {
69 | fontSize: 17,
70 | lineHeight: 24,
71 | textAlign: "center",
72 | },
73 | helpContainer: {
74 | marginTop: 15,
75 | marginHorizontal: 20,
76 | alignItems: "center",
77 | },
78 | helpLink: {
79 | paddingVertical: 15,
80 | },
81 | helpLinkText: {
82 | textAlign: "center",
83 | },
84 | });
85 |
--------------------------------------------------------------------------------
/Example/components/StyledText.tsx:
--------------------------------------------------------------------------------
1 | import { Text, TextProps } from "./Themed";
2 |
3 | export function MonoText(props: TextProps) {
4 | return (
5 |
6 | );
7 | }
8 |
--------------------------------------------------------------------------------
/Example/components/Themed.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Learn more about Light and Dark modes:
3 | * https://docs.expo.io/guides/color-schemes/
4 | */
5 |
6 | import { Text as DefaultText, View as DefaultView } from "react-native";
7 |
8 | import Colors from "../constants/Colors";
9 | import useColorScheme from "../hooks/useColorScheme";
10 |
11 | export function useThemeColor(
12 | props: { light?: string; dark?: string },
13 | colorName: keyof typeof Colors.light & keyof typeof Colors.dark
14 | ) {
15 | const theme = useColorScheme();
16 | const colorFromProps = props[theme];
17 |
18 | if (colorFromProps) {
19 | return colorFromProps;
20 | } else {
21 | return Colors[theme][colorName];
22 | }
23 | }
24 |
25 | type ThemeProps = {
26 | lightColor?: string;
27 | darkColor?: string;
28 | };
29 |
30 | export type TextProps = ThemeProps & DefaultText["props"];
31 | export type ViewProps = ThemeProps & DefaultView["props"];
32 |
33 | export function Text(props: TextProps) {
34 | const { style, lightColor, darkColor, ...otherProps } = props;
35 | const color = useThemeColor({ light: lightColor, dark: darkColor }, "text");
36 |
37 | return ;
38 | }
39 |
40 | export function View(props: ViewProps) {
41 | const { style, lightColor, darkColor, ...otherProps } = props;
42 | const backgroundColor = useThemeColor(
43 | { light: lightColor, dark: darkColor },
44 | "background"
45 | );
46 |
47 | return ;
48 | }
49 |
--------------------------------------------------------------------------------
/Example/components/__tests__/StyledText-test.js:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import renderer from "react-test-renderer";
3 |
4 | import { MonoText } from "../StyledText";
5 |
6 | it(`renders correctly`, () => {
7 | const tree = renderer.create(Snapshot test!).toJSON();
8 |
9 | expect(tree).toMatchSnapshot();
10 | });
11 |
--------------------------------------------------------------------------------
/Example/constants/Colors.ts:
--------------------------------------------------------------------------------
1 | const tintColorLight = "#2f95dc";
2 | const tintColorDark = "#fff";
3 |
4 | export default {
5 | light: {
6 | text: "#000",
7 | background: "#fff",
8 | tint: tintColorLight,
9 | tabIconDefault: "#ccc",
10 | tabIconSelected: tintColorLight,
11 | },
12 | dark: {
13 | text: "#fff",
14 | background: "#000",
15 | tint: tintColorDark,
16 | tabIconDefault: "#ccc",
17 | tabIconSelected: tintColorDark,
18 | },
19 | };
20 |
--------------------------------------------------------------------------------
/Example/constants/Layout.ts:
--------------------------------------------------------------------------------
1 | import { Dimensions } from "react-native";
2 |
3 | const width = Dimensions.get("window").width;
4 | const height = Dimensions.get("window").height;
5 |
6 | export default {
7 | window: {
8 | width,
9 | height,
10 | },
11 | isSmallDevice: width < 375,
12 | };
13 |
--------------------------------------------------------------------------------
/Example/hooks/useCachedResources.ts:
--------------------------------------------------------------------------------
1 | import { FontAwesome } from "@expo/vector-icons";
2 | import * as Font from "expo-font";
3 | import * as SplashScreen from "expo-splash-screen";
4 | import { useEffect, useState } from "react";
5 |
6 | export default function useCachedResources() {
7 | const [isLoadingComplete, setLoadingComplete] = useState(false);
8 |
9 | // Load any resources or data that we need prior to rendering the app
10 | useEffect(() => {
11 | async function loadResourcesAndDataAsync() {
12 | try {
13 | SplashScreen.preventAutoHideAsync();
14 |
15 | // Load fonts
16 | await Font.loadAsync({
17 | ...FontAwesome.font,
18 | "space-mono": require("../assets/fonts/SpaceMono-Regular.ttf"),
19 | });
20 | } catch (e) {
21 | // We might want to provide this error information to an error reporting service
22 | console.warn(e);
23 | } finally {
24 | setLoadingComplete(true);
25 | SplashScreen.hideAsync();
26 | }
27 | }
28 |
29 | loadResourcesAndDataAsync();
30 | }, []);
31 |
32 | return isLoadingComplete;
33 | }
34 |
--------------------------------------------------------------------------------
/Example/hooks/useColorScheme.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ColorSchemeName,
3 | useColorScheme as _useColorScheme,
4 | } from "react-native";
5 |
6 | // The useColorScheme value is always either light or dark, but the built-in
7 | // type suggests that it can be null. This will not happen in practice, so this
8 | // makes it a bit easier to work with.
9 | export default function useColorScheme(): NonNullable {
10 | return _useColorScheme() as NonNullable;
11 | }
12 |
--------------------------------------------------------------------------------
/Example/metro.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Metro configuration for React Native
3 | * https://github.com/facebook/react-native
4 | *
5 | * @format
6 | */
7 | const path = require("path");
8 | const extraNodeModules = {
9 | "react-native-draggable-flatlist": path.resolve(__dirname + "/../src"),
10 | };
11 | const watchFolders = [path.resolve(__dirname + "/../src")];
12 | module.exports = {
13 | transformer: {
14 | getTransformOptions: async () => ({
15 | transform: {
16 | experimentalImportSupport: false,
17 | inlineRequires: false,
18 | },
19 | }),
20 | },
21 | resolver: {
22 | extraNodeModules: new Proxy(extraNodeModules, {
23 | get: (target, name) =>
24 | //redirects dependencies referenced from target/ to local node_modules
25 | name in target
26 | ? target[name]
27 | : path.join(process.cwd(), `node_modules/${name}`),
28 | }),
29 | },
30 | watchFolders,
31 | };
32 |
--------------------------------------------------------------------------------
/Example/navigation/LinkingConfiguration.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Learn more about deep linking with React Navigation
3 | * https://reactnavigation.org/docs/deep-linking
4 | * https://reactnavigation.org/docs/configuring-links
5 | */
6 |
7 | import { LinkingOptions } from "@react-navigation/native";
8 | import * as Linking from "expo-linking";
9 |
10 | import { RootStackParamList } from "../types";
11 |
12 | const linking: LinkingOptions = {
13 | prefixes: [Linking.makeUrl("/")],
14 | config: {
15 | screens: {
16 | Root: {
17 | screens: {
18 | TabOne: {
19 | screens: {
20 | Basic: "basic",
21 | },
22 | },
23 | TabTwo: {
24 | screens: {
25 | Swipeable: "swipeable",
26 | },
27 | },
28 | },
29 | },
30 | NotFound: "*",
31 | },
32 | },
33 | };
34 |
35 | export default linking;
36 |
--------------------------------------------------------------------------------
/Example/navigation/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * If you are not familiar with React Navigation, refer to the "Fundamentals" guide:
3 | * https://reactnavigation.org/docs/getting-started
4 | *
5 | */
6 | import { FontAwesome } from "@expo/vector-icons";
7 | import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
8 | import {
9 | NavigationContainer,
10 | DefaultTheme,
11 | DarkTheme,
12 | } from "@react-navigation/native";
13 | import { createNativeStackNavigator } from "@react-navigation/native-stack";
14 | import * as React from "react";
15 | import { ColorSchemeName, Pressable } from "react-native";
16 |
17 | import Colors from "../constants/Colors";
18 | import useColorScheme from "../hooks/useColorScheme";
19 | import NotFoundScreen from "../screens/NotFoundScreen";
20 | import BasicScreen from "../screens/BasicScreen";
21 | import SwipeableScreen from "../screens/SwipeableScreen";
22 | import {
23 | RootStackParamList,
24 | RootTabParamList,
25 | RootTabScreenProps,
26 | } from "../types";
27 | import LinkingConfiguration from "./LinkingConfiguration";
28 | import NestedScreen from "../screens/NestedScreen";
29 | import HorizontalScreen from "../screens/HorizontalScreen";
30 |
31 | export default function Navigation({
32 | colorScheme,
33 | }: {
34 | colorScheme: ColorSchemeName;
35 | }) {
36 | return (
37 |
41 |
42 |
43 | );
44 | }
45 |
46 | /**
47 | * A root stack navigator is often used for displaying modals on top of all other content.
48 | * https://reactnavigation.org/docs/modal
49 | */
50 | const Stack = createNativeStackNavigator();
51 |
52 | function RootNavigator() {
53 | return (
54 |
55 |
60 |
61 | );
62 | }
63 |
64 | /**
65 | * A bottom tab navigator displays tab buttons on the bottom of the display to switch screens.
66 | * https://reactnavigation.org/docs/bottom-tab-navigator
67 | */
68 | const BottomTab = createBottomTabNavigator();
69 |
70 | function BottomTabNavigator() {
71 | const colorScheme = useColorScheme();
72 |
73 | return (
74 |
80 | ) => ({
84 | title: "Basic",
85 | tabBarIcon: ({ color }) => ,
86 | })}
87 | />
88 | (
94 |
95 | ),
96 | }}
97 | />
98 | ,
104 | }}
105 | />
106 | (
112 |
113 | ),
114 | }}
115 | />
116 |
117 | );
118 | }
119 |
120 | /**
121 | * You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
122 | */
123 | function TabBarIcon(props: {
124 | name: React.ComponentProps["name"];
125 | color: string;
126 | }) {
127 | return ;
128 | }
129 |
--------------------------------------------------------------------------------
/Example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "version": "1.0.0",
4 | "main": "node_modules/expo/AppEntry.js",
5 | "scripts": {
6 | "start": "expo start",
7 | "android": "expo start --android",
8 | "ios": "expo start --ios",
9 | "web": "expo start --web",
10 | "eject": "expo eject",
11 | "test": "jest --watchAll"
12 | },
13 | "jest": {
14 | "preset": "jest-expo"
15 | },
16 | "dependencies": {
17 | "@expo/vector-icons": "^13.0.0",
18 | "@react-navigation/bottom-tabs": "^6.0.5",
19 | "@react-navigation/native": "^6.0.2",
20 | "@react-navigation/native-stack": "^6.1.0",
21 | "expo": "^48.0.0",
22 | "expo-asset": "~8.9.1",
23 | "expo-constants": "~14.2.1",
24 | "expo-font": "~11.1.1",
25 | "expo-linking": "~4.0.1",
26 | "expo-splash-screen": "~0.18.1",
27 | "expo-status-bar": "~1.4.4",
28 | "expo-system-ui": "~2.2.1",
29 | "expo-web-browser": "~12.1.1",
30 | "react": "18.2.0",
31 | "react-dom": "18.2.0",
32 | "react-native": "0.71.3",
33 | "react-native-gesture-handler": "~2.9.0",
34 | "react-native-reanimated": "~2.14.4",
35 | "react-native-safe-area-context": "4.5.0",
36 | "react-native-screens": "~3.20.0",
37 | "react-native-swipeable-item": "^2.0.2",
38 | "react-native-web": "~0.18.11"
39 | },
40 | "devDependencies": {
41 | "@babel/core": "^7.20.0",
42 | "@types/react": "~18.0.27",
43 | "@types/react-native": "~0.66.13",
44 | "jest": "^29.2.1",
45 | "jest-expo": "^48.0.0",
46 | "react-test-renderer": "17.0.2",
47 | "typescript": "^4.9.4"
48 | },
49 | "private": true
50 | }
51 |
--------------------------------------------------------------------------------
/Example/screens/BasicScreen.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from "react";
2 | import { Text, View, StyleSheet, TouchableOpacity } from "react-native";
3 | import DraggableFlatList, {
4 | ScaleDecorator,
5 | ShadowDecorator,
6 | OpacityDecorator,
7 | RenderItemParams,
8 | } from "react-native-draggable-flatlist";
9 |
10 | import { mapIndexToData, Item } from "../utils";
11 |
12 | const NUM_ITEMS = 100;
13 |
14 | const initialData: Item[] = [...Array(NUM_ITEMS)].map(mapIndexToData);
15 |
16 | export default function Basic() {
17 | const [data, setData] = useState(initialData);
18 |
19 | const renderItem = useCallback(
20 | ({ item, drag, isActive }: RenderItemParams- ) => {
21 | return (
22 |
23 |
24 |
25 |
34 | {item.text}
35 |
36 |
37 |
38 |
39 | );
40 | },
41 | []
42 | );
43 |
44 | return (
45 | setData(data)}
48 | keyExtractor={(item) => item.key}
49 | renderItem={renderItem}
50 | renderPlaceholder={() => (
51 |
52 | )}
53 | />
54 | );
55 | }
56 |
57 | const styles = StyleSheet.create({
58 | rowItem: {
59 | height: 100,
60 | alignItems: "center",
61 | justifyContent: "center",
62 | },
63 | text: {
64 | color: "white",
65 | fontSize: 24,
66 | fontWeight: "bold",
67 | textAlign: "center",
68 | },
69 | });
70 |
--------------------------------------------------------------------------------
/Example/screens/HorizontalScreen.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useState } from "react";
2 | import { Text, View, StyleSheet, TouchableOpacity } from "react-native";
3 | import DraggableFlatList, {
4 | ScaleDecorator,
5 | RenderItemParams,
6 | } from "react-native-draggable-flatlist";
7 |
8 | import { mapIndexToData, Item } from "../utils";
9 |
10 | const NUM_ITEMS = 100;
11 |
12 | const initialData: Item[] = [...Array(NUM_ITEMS)].map(mapIndexToData);
13 |
14 | export default function Horizontal() {
15 | const [data, setData] = useState(initialData);
16 |
17 | const renderItem = useCallback(
18 | ({ item, drag, isActive }: RenderItemParams
- ) => {
19 | return (
20 |
21 |
31 | {item.text}
32 |
33 |
34 | );
35 | },
36 | []
37 | );
38 |
39 | return (
40 | setData(data)}
44 | keyExtractor={(item) => {
45 | return item.key;
46 | }}
47 | renderItem={renderItem}
48 | renderPlaceholder={() => (
49 |
50 | )}
51 | />
52 | );
53 | }
54 |
55 | const styles = StyleSheet.create({
56 | rowItem: {
57 | height: 100,
58 | width: 100,
59 | alignItems: "center",
60 | justifyContent: "center",
61 | },
62 | text: {
63 | color: "white",
64 | fontSize: 24,
65 | fontWeight: "bold",
66 | textAlign: "center",
67 | },
68 | });
69 |
--------------------------------------------------------------------------------
/Example/screens/NestedScreen.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useCallback } from "react";
2 | import { Text, View, StyleSheet, TouchableOpacity } from "react-native";
3 |
4 | import {
5 | RenderItemParams,
6 | ScaleDecorator,
7 | ShadowDecorator,
8 | NestableScrollContainer,
9 | NestableDraggableFlatList,
10 | } from "react-native-draggable-flatlist";
11 |
12 | import { mapIndexToData, Item } from "../utils";
13 |
14 | const NUM_ITEMS = 6;
15 | const initialData1 = [...Array(NUM_ITEMS)].fill(0).map(mapIndexToData);
16 | const initialData2 = [...Array(NUM_ITEMS)].fill(0).map(mapIndexToData);
17 | const initialData3 = [...Array(NUM_ITEMS)].fill(0).map(mapIndexToData);
18 |
19 | function NestedDraggableListScreen() {
20 | const [data1, setData1] = useState(initialData1);
21 | const [data2, setData2] = useState(initialData2);
22 | const [data3, setData3] = useState(initialData3);
23 |
24 | const renderItem = useCallback((params: RenderItemParams
- ) => {
25 | return (
26 |
27 |
28 |
29 |
30 |
31 | );
32 | }, []);
33 |
34 | const keyExtractor = (item) => item.key;
35 |
36 | return (
37 |
38 |
39 |
40 | setData1(data)}
45 | />
46 |
47 | setData2(data)}
52 | />
53 |
54 | setData3(data)}
59 | />
60 |
61 |
62 | );
63 | }
64 |
65 | function Header({ text }: { text: string }) {
66 | return (
67 |
68 |
69 | {text}
70 |
71 |
72 | );
73 | }
74 |
75 | type RowItemProps = {
76 | item: Item;
77 | drag: () => void;
78 | };
79 |
80 | function RowItem({ item, drag }: RowItemProps) {
81 | return (
82 |
94 | {item.text}
95 |
96 | );
97 | }
98 |
99 | export default NestedDraggableListScreen;
100 |
101 | const styles = StyleSheet.create({
102 | container: {
103 | flex: 1,
104 | backgroundColor: "seashell",
105 | paddingTop: 44,
106 | },
107 | row: {
108 | flexDirection: "row",
109 | flex: 1,
110 | alignItems: "center",
111 | justifyContent: "center",
112 | padding: 15,
113 | },
114 | text: {
115 | fontWeight: "bold",
116 | color: "white",
117 | fontSize: 32,
118 | },
119 | });
120 |
--------------------------------------------------------------------------------
/Example/screens/NotFoundScreen.tsx:
--------------------------------------------------------------------------------
1 | import { StyleSheet, TouchableOpacity } from "react-native";
2 |
3 | import { Text, View } from "../components/Themed";
4 | import { RootStackScreenProps } from "../types";
5 |
6 | export default function NotFoundScreen({
7 | navigation,
8 | }: RootStackScreenProps<"NotFound">) {
9 | return (
10 |
11 | This screen doesn't exist.
12 | navigation.replace("Root")}
14 | style={styles.link}
15 | >
16 | Go to home screen!
17 |
18 |
19 | );
20 | }
21 |
22 | const styles = StyleSheet.create({
23 | container: {
24 | flex: 1,
25 | alignItems: "center",
26 | justifyContent: "center",
27 | padding: 20,
28 | },
29 | title: {
30 | fontSize: 20,
31 | fontWeight: "bold",
32 | },
33 | link: {
34 | marginTop: 15,
35 | paddingVertical: 15,
36 | },
37 | linkText: {
38 | fontSize: 14,
39 | color: "#2e78b7",
40 | },
41 | });
42 |
--------------------------------------------------------------------------------
/Example/screens/SwipeableScreen.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useRef, useCallback } from "react";
2 | import {
3 | Text,
4 | View,
5 | StyleSheet,
6 | LayoutAnimation,
7 | TouchableOpacity,
8 | } from "react-native";
9 | import Animated, { useAnimatedStyle } from "react-native-reanimated";
10 | import DraggableFlatList, {
11 | RenderItemParams,
12 | ScaleDecorator,
13 | } from "react-native-draggable-flatlist";
14 | import SwipeableItem, {
15 | useSwipeableItemParams,
16 | } from "react-native-swipeable-item";
17 | import { mapIndexToData, Item } from "../utils";
18 |
19 | const OVERSWIPE_DIST = 20;
20 | const NUM_ITEMS = 20;
21 |
22 | const initialData: Item[] = [...Array(NUM_ITEMS)].fill(0).map(mapIndexToData);
23 |
24 | function App() {
25 | const [data, setData] = useState(initialData);
26 | const itemRefs = useRef(new Map());
27 |
28 | const renderItem = useCallback((params: RenderItemParams
- ) => {
29 | const onPressDelete = () => {
30 | LayoutAnimation.configureNext(LayoutAnimation.Presets.spring);
31 | setData((prev) => {
32 | return prev.filter((item) => item !== params.item);
33 | });
34 | };
35 |
36 | return (
37 |
38 | );
39 | }, []);
40 |
41 | return (
42 |
43 | item.key}
45 | data={data}
46 | renderItem={renderItem}
47 | onDragEnd={({ data }) => setData(data)}
48 | activationDistance={20}
49 | />
50 |
51 | );
52 | }
53 |
54 | export default App;
55 |
56 | type RowItemProps = {
57 | item: Item;
58 | drag: () => void;
59 | onPressDelete: () => void;
60 | itemRefs: React.MutableRefObject