From 01f791e42874a5c9bf1b18df029e32c30a51e8b5 Mon Sep 17 00:00:00 2001 From: Mo Gorhom Date: Sun, 16 May 2021 12:07:55 +0100 Subject: [PATCH] refactor: create one generic scrollable component (#442) * chore: updated react-native in example * chore: updated react/react native types * chore: updated scrollable hooks * chore: deleted old scrollable components * refactor: added the new generic scrollable method --- example/package.json | 4 +- example/yarn.lock | 16 +- package.json | 4 +- src/components/bottomSheet/BottomSheet.tsx | 1 + .../BottomSheetFlatList.tsx | 27 +++ .../BottomSheetScrollView.tsx | 27 +++ .../BottomSheetSectionList.tsx | 27 +++ .../createBottomSheetScrollableComponent.tsx} | 55 ++--- src/components/bottomSheetScrollable/index.ts | 9 + .../styles.ts | 0 .../bottomSheetScrollable/types.d.ts | 211 ++++++++++++++++++ src/components/flatList/index.ts | 1 - src/components/flatList/types.d.ts | 83 ------- src/components/scrollView/ScrollView.tsx | 84 ------- src/components/scrollView/index.ts | 1 - src/components/scrollView/styles.ts | 7 - src/components/scrollView/types.d.ts | 69 ------ src/components/sectionList/SectionList.tsx | 85 ------- src/components/sectionList/index.ts | 1 - src/components/sectionList/styles.ts | 7 - src/components/sectionList/types.d.ts | 48 ---- src/hooks/useScrollable.ts | 3 +- src/hooks/useScrollableInternal.ts | 6 +- src/index.ts | 51 +++-- src/types.d.ts | 1 - yarn.lock | 22 +- 26 files changed, 379 insertions(+), 471 deletions(-) create mode 100644 src/components/bottomSheetScrollable/BottomSheetFlatList.tsx create mode 100644 src/components/bottomSheetScrollable/BottomSheetScrollView.tsx create mode 100644 src/components/bottomSheetScrollable/BottomSheetSectionList.tsx rename src/components/{flatList/FlatList.tsx => bottomSheetScrollable/createBottomSheetScrollableComponent.tsx} (62%) create mode 100644 src/components/bottomSheetScrollable/index.ts rename src/components/{flatList => bottomSheetScrollable}/styles.ts (100%) create mode 100644 src/components/bottomSheetScrollable/types.d.ts delete mode 100644 src/components/flatList/index.ts delete mode 100644 src/components/flatList/types.d.ts delete mode 100644 src/components/scrollView/ScrollView.tsx delete mode 100644 src/components/scrollView/index.ts delete mode 100644 src/components/scrollView/styles.ts delete mode 100644 src/components/scrollView/types.d.ts delete mode 100644 src/components/sectionList/SectionList.tsx delete mode 100644 src/components/sectionList/index.ts delete mode 100644 src/components/sectionList/styles.ts delete mode 100644 src/components/sectionList/types.d.ts diff --git a/example/package.json b/example/package.json index e9d1ee34b..0e246e4aa 100644 --- a/example/package.json +++ b/example/package.json @@ -34,8 +34,8 @@ "@babel/core": "^7.13.10", "@babel/runtime": "^7.13.10", "@types/faker": "^4.1.12", - "@types/react": "^17.0.3", - "@types/react-native": "^0.64.4", + "@types/react": "^17.0.5", + "@types/react-native": "^0.64.5", "metro-react-native-babel-preset": "^0.65.2", "typescript": "^4.2.4" } diff --git a/example/yarn.lock b/example/yarn.lock index 6805ab8e5..82dc1fc24 100644 --- a/example/yarn.lock +++ b/example/yarn.lock @@ -1312,10 +1312,10 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== -"@types/react-native@^0.64.4": - version "0.64.4" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.4.tgz#9f11bef7dd5520801884829c73b19d75aa42e73c" - integrity sha512-VqnlmadGkD5usREvnuyVpWDS1W8f6cCz6MP5fZdgONsaZ9/Ijfb9Iq9MZ5O3bnW1OyJixDX9HtSp3COsFSLD8Q== +"@types/react-native@^0.64.5": + version "0.64.5" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.5.tgz#219738b52b2e372ec057d3c8f20fbd6c37b245cd" + integrity sha512-k0r8MnQX7UFboZDvMKLov26gFLXKrNgLhCfSVhjaZ6wMUofKijxvee7/wgfAqtT2zS5FR4an4+qn0r72SCbw3g== dependencies: "@types/react" "*" @@ -1327,10 +1327,10 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/react@^17.0.3": - version "17.0.3" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.3.tgz#ba6e215368501ac3826951eef2904574c262cc79" - integrity sha512-wYOUxIgs2HZZ0ACNiIayItyluADNbONl7kt8lkLjVK8IitMH5QMyAh75Fwhmo37r1m7L2JaFj03sIfxBVDvRAg== +"@types/react@^17.0.5": + version "17.0.5" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.5.tgz#3d887570c4489011f75a3fc8f965bf87d09a1bea" + integrity sha512-bj4biDB9ZJmGAYTWSKJly6bMr4BLUiBrx9ujiJEoP9XIDY9CTaPGxE5QWN/1WjpPLzYF7/jRNnV2nNxNe970sw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" diff --git a/package.json b/package.json index 35120c831..54d34bb36 100644 --- a/package.json +++ b/package.json @@ -48,8 +48,8 @@ "@react-native-community/eslint-config": "^2.0.0", "@release-it/conventional-changelog": "^2.0.1", "@types/invariant": "^2.2.34", - "@types/react": "^16.9.46", - "@types/react-native": "^0.63.8", + "@types/react": "^17.0.5", + "@types/react-native": "^0.64.5", "auto-changelog": "^2.2.1", "copyfiles": "^2.4.1", "eslint": "^7.26.0", diff --git a/src/components/bottomSheet/BottomSheet.tsx b/src/components/bottomSheet/BottomSheet.tsx index dd114d034..1ef531858 100644 --- a/src/components/bottomSheet/BottomSheet.tsx +++ b/src/components/bottomSheet/BottomSheet.tsx @@ -1063,6 +1063,7 @@ const BottomSheetComponent = forwardRef( animatedPosition, animatedSheetHeight, animatedSheetState, + animatedScrollableState, animatedContainerHeight, animatedHandleHeight, isLayoutCalculated, diff --git a/src/components/bottomSheetScrollable/BottomSheetFlatList.tsx b/src/components/bottomSheetScrollable/BottomSheetFlatList.tsx new file mode 100644 index 000000000..dc9cf24a3 --- /dev/null +++ b/src/components/bottomSheetScrollable/BottomSheetFlatList.tsx @@ -0,0 +1,27 @@ +import { memo } from 'react'; +import { + FlatList as RNFlatList, + FlatListProps as RNFlatListProps, +} from 'react-native'; +import Animated from 'react-native-reanimated'; +import { createBottomSheetScrollableComponent } from './createBottomSheetScrollableComponent'; +import type { + BottomSheetFlatListMethods, + BottomSheetFlatListProps, +} from './types'; + +const AnimatedFlatList = Animated.createAnimatedComponent>( + RNFlatList +); + +const BottomSheetFlatListComponent = createBottomSheetScrollableComponent< + BottomSheetFlatListMethods, + BottomSheetFlatListProps +>(AnimatedFlatList); + +const BottomSheetFlatList = memo(BottomSheetFlatListComponent); +BottomSheetFlatList.displayName = 'BottomSheetFlatList'; + +export default BottomSheetFlatList as ( + props: BottomSheetFlatListProps +) => ReturnType; diff --git a/src/components/bottomSheetScrollable/BottomSheetScrollView.tsx b/src/components/bottomSheetScrollable/BottomSheetScrollView.tsx new file mode 100644 index 000000000..ed5d377f9 --- /dev/null +++ b/src/components/bottomSheetScrollable/BottomSheetScrollView.tsx @@ -0,0 +1,27 @@ +import { memo } from 'react'; +import { + ScrollView as RNScrollView, + ScrollViewProps as RNScrollViewProps, +} from 'react-native'; +import Animated from 'react-native-reanimated'; +import { createBottomSheetScrollableComponent } from './createBottomSheetScrollableComponent'; +import type { + BottomSheetScrollViewMethods, + BottomSheetScrollViewProps, +} from './types'; + +const AnimatedScrollView = Animated.createAnimatedComponent( + RNScrollView +); + +const BottomSheetScrollViewComponent = createBottomSheetScrollableComponent< + BottomSheetScrollViewMethods, + BottomSheetScrollViewProps +>(AnimatedScrollView); + +const BottomSheetScrollView = memo(BottomSheetScrollViewComponent); +BottomSheetScrollView.displayName = 'BottomSheetScrollView'; + +export default BottomSheetScrollView as ( + props: BottomSheetScrollViewProps +) => ReturnType; diff --git a/src/components/bottomSheetScrollable/BottomSheetSectionList.tsx b/src/components/bottomSheetScrollable/BottomSheetSectionList.tsx new file mode 100644 index 000000000..46b7c1311 --- /dev/null +++ b/src/components/bottomSheetScrollable/BottomSheetSectionList.tsx @@ -0,0 +1,27 @@ +import { memo } from 'react'; +import { + SectionList as RNSectionList, + SectionListProps as RNSectionListProps, +} from 'react-native'; +import Animated from 'react-native-reanimated'; +import { createBottomSheetScrollableComponent } from './createBottomSheetScrollableComponent'; +import type { + BottomSheetSectionListMethods, + BottomSheetSectionListProps, +} from './types'; + +const AnimatedSectionList = Animated.createAnimatedComponent< + RNSectionListProps +>(RNSectionList); + +const BottomSheetSectionListComponent = createBottomSheetScrollableComponent< + BottomSheetSectionListMethods, + BottomSheetSectionListProps +>(AnimatedSectionList); + +const BottomSheetSectionList = memo(BottomSheetSectionListComponent); +BottomSheetSectionList.displayName = 'BottomSheetSectionList'; + +export default BottomSheetSectionList as ( + props: BottomSheetSectionListProps +) => ReturnType; diff --git a/src/components/flatList/FlatList.tsx b/src/components/bottomSheetScrollable/createBottomSheetScrollableComponent.tsx similarity index 62% rename from src/components/flatList/FlatList.tsx rename to src/components/bottomSheetScrollable/createBottomSheetScrollableComponent.tsx index 788dde9d3..ee7381c92 100644 --- a/src/components/flatList/FlatList.tsx +++ b/src/components/bottomSheetScrollable/createBottomSheetScrollableComponent.tsx @@ -1,58 +1,46 @@ import React, { forwardRef, - Ref, - useRef, - useImperativeHandle, useEffect, - memo, + useImperativeHandle, + useRef, } from 'react'; -import { - FlatList as RNFlatList, - FlatListProps as RNFlatListProps, -} from 'react-native'; -import Animated from 'react-native-reanimated'; import { NativeViewGestureHandler } from 'react-native-gesture-handler'; import BottomSheetDraggableView from '../bottomSheetDraggableView'; import { useScrollableInternal, useBottomSheetInternal } from '../../hooks'; -import type { - BottomSheetFlatListProps, - BottomSheetFlatListType, -} from './types'; import { styles } from './styles'; -const AnimatedFlatList = Animated.createAnimatedComponent>( - RNFlatList -); - -const BottomSheetFlatListName = 'FlatList'; - -const BottomSheetFlatListComponent = forwardRef( - (props: BottomSheetFlatListProps, ref: Ref) => { +export function createBottomSheetScrollableComponent( + ScrollableComponent: any +) { + return forwardRef((props, ref) => { // props const { focusHook: useFocusHook = useEffect, overScrollMode = 'never', ...rest - } = props; + }: any = props; - // refs + //#region refs const nativeGestureRef = useRef(null); + //#endregion - // hooks + //#region hooks const { scrollableRef, scrollableAnimatedProps, handleScrollEvent, handleSettingScrollable, - } = useScrollableInternal(BottomSheetFlatListName); + } = useScrollableInternal(); const { enableContentPanningGesture } = useBottomSheetInternal(); + //#endregion - // effects + //#region effects // @ts-ignore useImperativeHandle(ref, () => scrollableRef.current); useFocusHook(handleSettingScrollable); + //#endregion - // render + //#region render return ( - ); - } -); - -const BottomSheetFlatList = memo(BottomSheetFlatListComponent); -BottomSheetFlatList.displayName = 'BottomSheetFlatList'; - -export default (BottomSheetFlatList as any) as typeof BottomSheetFlatListType; + //#endregion + }); +} diff --git a/src/components/bottomSheetScrollable/index.ts b/src/components/bottomSheetScrollable/index.ts new file mode 100644 index 000000000..41f2c18f2 --- /dev/null +++ b/src/components/bottomSheetScrollable/index.ts @@ -0,0 +1,9 @@ +export { default as BottomSheetSectionList } from './BottomSheetSectionList'; +export { default as BottomSheetFlatList } from './BottomSheetFlatList'; +export { default as BottomSheetScrollView } from './BottomSheetScrollView'; + +export type { + BottomSheetFlatListMethods, + BottomSheetScrollViewMethods, + BottomSheetSectionListMethods, +} from './types'; diff --git a/src/components/flatList/styles.ts b/src/components/bottomSheetScrollable/styles.ts similarity index 100% rename from src/components/flatList/styles.ts rename to src/components/bottomSheetScrollable/styles.ts diff --git a/src/components/bottomSheetScrollable/types.d.ts b/src/components/bottomSheetScrollable/types.d.ts new file mode 100644 index 000000000..4f2c2bde4 --- /dev/null +++ b/src/components/bottomSheetScrollable/types.d.ts @@ -0,0 +1,211 @@ +import type { ReactNode, Ref } from 'react'; +import type { + ScrollViewProps, + FlatListProps, + SectionListProps, +} from 'react-native'; +import type Animated from 'react-native-reanimated'; + +export interface BottomSheetScrollableProps { + /** + * This needed when bottom sheet used with multiple scrollables to allow bottom sheet + * detect the current scrollable ref, especially when used with `React Navigation`. + * You will need to provide `useFocusEffect` from `@react-navigation/native`. + * @type (effect: EffectCallback, deps?: DependencyList) => void + */ + focusHook?: (effect: EffectCallback, deps?: DependencyList) => void; + // contentContainerStyle?: StyleProp< + // Animated.AnimateStyle> + // >; +} + +export type ScrollableProps = + | ScrollViewProps + | FlatListProps + | SectionListProps; + +//#region FlatList +export type BottomSheetFlatListProps = Omit< + Animated.AnimateProps>, + 'decelerationRate' | 'onScrollBeginDrag' | 'scrollEventThrottle' +> & + BottomSheetScrollableProps & { + ref?: Ref; + }; + +export interface BottomSheetFlatListMethods { + /** + * Scrolls to the end of the content. May be janky without `getItemLayout` prop. + */ + scrollToEnd: (params?: { animated?: boolean | null }) => void; + + /** + * Scrolls to the item at the specified index such that it is positioned in the viewable area + * such that viewPosition 0 places it at the top, 1 at the bottom, and 0.5 centered in the middle. + * Cannot scroll to locations outside the render window without specifying the getItemLayout prop. + */ + scrollToIndex: (params: { + animated?: boolean | null; + index: number; + viewOffset?: number; + viewPosition?: number; + }) => void; + + /** + * Requires linear scan through data - use `scrollToIndex` instead if possible. + * May be janky without `getItemLayout` prop. + */ + scrollToItem: (params: { + animated?: boolean | null; + item: ItemT; + viewPosition?: number; + }) => void; + + /** + * Scroll to a specific content pixel offset, like a normal `ScrollView`. + */ + scrollToOffset: (params: { + animated?: boolean | null; + offset: number; + }) => void; + + /** + * Tells the list an interaction has occured, which should trigger viewability calculations, + * e.g. if waitForInteractions is true and the user has not scrolled. This is typically called + * by taps on items or by navigation actions. + */ + recordInteraction: () => void; + + /** + * Displays the scroll indicators momentarily. + */ + flashScrollIndicators: () => void; + + /** + * Provides a handle to the underlying scroll responder. + */ + getScrollResponder: () => JSX.Element | null | undefined; + + /** + * Provides a reference to the underlying host component + */ + getNativeScrollRef: () => + | React.RefObject + | React.RefObject + | null + | undefined; + + getScrollableNode: () => any; + + // TODO: use `unknown` instead of `any` for Typescript >= 3.0 + setNativeProps: (props: { [key: string]: any }) => void; +} +//#endregion + +//#region ScrollView +export type BottomSheetScrollViewProps = Omit< + Animated.AnimateProps, + 'decelerationRate' | 'onScrollBeginDrag' | 'scrollEventThrottle' +> & + BottomSheetScrollableProps & { + ref?: Ref; + children: ReactNode | ReactNode[]; + }; + +export interface BottomSheetScrollViewMethods { + /** + * Scrolls to a given x, y offset, either immediately or with a smooth animation. + * Syntax: + * + * scrollTo(options: {x: number = 0; y: number = 0; animated: boolean = true}) + * + * Note: The weird argument signature is due to the fact that, for historical reasons, + * the function also accepts separate arguments as an alternative to the options object. + * This is deprecated due to ambiguity (y before x), and SHOULD NOT BE USED. + */ + scrollTo( + y?: number | { x?: number; y?: number; animated?: boolean }, + x?: number, + animated?: boolean + ): void; + + /** + * A helper function that scrolls to the end of the scrollview; + * If this is a vertical ScrollView, it scrolls to the bottom. + * If this is a horizontal ScrollView scrolls to the right. + * + * The options object has an animated prop, that enables the scrolling animation or not. + * The animated prop defaults to true + */ + scrollToEnd(options?: { animated: boolean }): void; + + /** + * Returns a reference to the underlying scroll responder, which supports + * operations like `scrollTo`. All ScrollView-like components should + * implement this method so that they can be composed while providing access + * to the underlying scroll responder's methods. + */ + getScrollResponder(): JSX.Element; + + getScrollableNode(): any; + + // Undocumented + getInnerViewNode(): any; + + /** + * @deprecated Use scrollTo instead + */ + scrollWithoutAnimationTo?: (y: number, x: number) => void; + + /** + * This function sends props straight to native. They will not participate in + * future diff process - this means that if you do not include them in the + * next render, they will remain active (see [Direct + * Manipulation](https://reactnative.dev/docs/direct-manipulation)). + */ + setNativeProps(nativeProps: object): void; +} +//#endregion + +//#region SectionList +type BottomSheetSectionListProps = Omit< + Animated.AnimateProps>, + 'decelerationRate' | 'onScrollBeginDrag' | 'scrollEventThrottle' +> & + BottomSheetScrollableProps & { + ref?: Ref; + }; + +export interface BottomSheetSectionListMethods { + /** + * Scrolls to the item at the specified sectionIndex and itemIndex (within the section) + * positioned in the viewable area such that viewPosition 0 places it at the top + * (and may be covered by a sticky header), 1 at the bottom, and 0.5 centered in the middle. + */ + scrollToLocation(params: SectionListScrollParams): void; + + /** + * Tells the list an interaction has occurred, which should trigger viewability calculations, e.g. + * if `waitForInteractions` is true and the user has not scrolled. This is typically called by + * taps on items or by navigation actions. + */ + recordInteraction(): void; + + /** + * Displays the scroll indicators momentarily. + * + * @platform ios + */ + flashScrollIndicators(): void; + + /** + * Provides a handle to the underlying scroll responder. + */ + getScrollResponder(): ScrollView | undefined; + + /** + * Provides a handle to the underlying scroll node. + */ + getScrollableNode(): NodeHandle | undefined; +} +//#endregion diff --git a/src/components/flatList/index.ts b/src/components/flatList/index.ts deleted file mode 100644 index bc7e47b75..000000000 --- a/src/components/flatList/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './FlatList'; diff --git a/src/components/flatList/types.d.ts b/src/components/flatList/types.d.ts deleted file mode 100644 index 6646480db..000000000 --- a/src/components/flatList/types.d.ts +++ /dev/null @@ -1,83 +0,0 @@ -import type { Component, EffectCallback, DependencyList } from 'react'; -import type { FlatListProps as RNFlatListProps } from 'react-native'; - -type BottomSheetFlatListProps = Omit< - RNFlatListProps, - 'decelerationRate' | 'onScrollBeginDrag' | 'scrollEventThrottle' -> & { - /** - * This needed when bottom sheet used with multiple scrollables to allow bottom sheet detect the current scrollable ref, especially when used with `React Navigation`. You will need to provide `useFocusEffect` from `@react-navigation/native`. - * @type (effect: EffectCallback, deps?: DependencyList) => void - */ - focusHook?: (effect: EffectCallback, deps?: DependencyList) => void; -}; - -export class BottomSheetFlatListType extends Component< - BottomSheetFlatListProps -> { - /** - * Scrolls to the end of the content. May be janky without `getItemLayout` prop. - */ - scrollToEnd: (params?: { animated?: boolean | null }) => void; - - /** - * Scrolls to the item at the specified index such that it is positioned in the viewable area - * such that viewPosition 0 places it at the top, 1 at the bottom, and 0.5 centered in the middle. - * Cannot scroll to locations outside the render window without specifying the getItemLayout prop. - */ - scrollToIndex: (params: { - animated?: boolean | null; - index: number; - viewOffset?: number; - viewPosition?: number; - }) => void; - - /** - * Requires linear scan through data - use `scrollToIndex` instead if possible. - * May be janky without `getItemLayout` prop. - */ - scrollToItem: (params: { - animated?: boolean | null; - item: ItemT; - viewPosition?: number; - }) => void; - - /** - * Scroll to a specific content pixel offset, like a normal `ScrollView`. - */ - scrollToOffset: (params: { - animated?: boolean | null; - offset: number; - }) => void; - - /** - * Tells the list an interaction has occured, which should trigger viewability calculations, - * e.g. if waitForInteractions is true and the user has not scrolled. This is typically called - * by taps on items or by navigation actions. - */ - recordInteraction: () => void; - - /** - * Displays the scroll indicators momentarily. - */ - flashScrollIndicators: () => void; - - /** - * Provides a handle to the underlying scroll responder. - */ - getScrollResponder: () => JSX.Element | null | undefined; - - /** - * Provides a reference to the underlying host component - */ - getNativeScrollRef: () => - | React.RefObject - | React.RefObject - | null - | undefined; - - getScrollableNode: () => any; - - // TODO: use `unknown` instead of `any` for Typescript >= 3.0 - setNativeProps: (props: { [key: string]: any }) => void; -} diff --git a/src/components/scrollView/ScrollView.tsx b/src/components/scrollView/ScrollView.tsx deleted file mode 100644 index 8ce52654a..000000000 --- a/src/components/scrollView/ScrollView.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React, { - useRef, - useImperativeHandle, - useEffect, - forwardRef, - Ref, - memo, -} from 'react'; -import { - ScrollView as RNScrollView, - ScrollViewProps as RNScrollViewProps, -} from 'react-native'; -import Animated from 'react-native-reanimated'; -import { NativeViewGestureHandler } from 'react-native-gesture-handler'; -import BottomSheetDraggableView from '../bottomSheetDraggableView'; -import { useScrollableInternal, useBottomSheetInternal } from '../../hooks'; -import type { - BottomSheetScrollViewType, - BottomSheetScrollViewProps, -} from './types'; -import { styles } from './styles'; - -const AnimatedScrollView = Animated.createAnimatedComponent( - RNScrollView -); - -const BottomSheetScrollViewName = 'ScrollView'; - -const BottomSheetScrollViewComponent = forwardRef( - (props: BottomSheetScrollViewProps, ref: Ref) => { - // props - const { - focusHook: useFocusHook = useEffect, - overScrollMode = 'never', - ...rest - } = props; - - // refs - const nativeGestureRef = useRef(null); - - // hooks - const { - scrollableRef, - scrollableAnimatedProps, - handleScrollEvent, - handleSettingScrollable, - } = useScrollableInternal(BottomSheetScrollViewName); - const { enableContentPanningGesture } = useBottomSheetInternal(); - - // effects - // @ts-ignore - useImperativeHandle(ref, () => scrollableRef.current); - useFocusHook(handleSettingScrollable); - - return ( - - - - - - ); - } -); - -const BottomSheetScrollView = memo(BottomSheetScrollViewComponent); -BottomSheetScrollView.displayName = 'BottomSheetScrollView'; - -export default (BottomSheetScrollView as any) as typeof BottomSheetScrollViewType; diff --git a/src/components/scrollView/index.ts b/src/components/scrollView/index.ts deleted file mode 100644 index 2a7445dbf..000000000 --- a/src/components/scrollView/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './ScrollView'; diff --git a/src/components/scrollView/styles.ts b/src/components/scrollView/styles.ts deleted file mode 100644 index 31e937d84..000000000 --- a/src/components/scrollView/styles.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { StyleSheet } from 'react-native'; - -export const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); diff --git a/src/components/scrollView/types.d.ts b/src/components/scrollView/types.d.ts deleted file mode 100644 index cf0ee48b8..000000000 --- a/src/components/scrollView/types.d.ts +++ /dev/null @@ -1,69 +0,0 @@ -import type { Component, EffectCallback, DependencyList } from 'react'; -import type { - ScrollViewProps as RNScrollViewProps, - ScrollResponderMixin, -} from 'react-native'; - -export type BottomSheetScrollViewProps = Omit< - RNScrollViewProps, - 'decelerationRate' | 'onScrollBeginDrag' | 'scrollEventThrottle' -> & { - children: React.ReactNode[] | React.ReactNode; - /** - * This needed when bottom sheet used with multiple scrollables to allow bottom sheet detect the current scrollable ref, especially when used with `React Navigation`. You will need to provide `useFocusEffect` from `@react-navigation/native`. - * @type (effect: EffectCallback, deps?: DependencyList) => void - */ - focusHook?: (effect: EffectCallback, deps?: DependencyList) => void; -}; - -type Constructor = new (...args: any[]) => T; - -declare class BottomSheetScrollViewComponent extends Component {} -declare const BottomSheetScrollViewBase: Constructor & - typeof BottomSheetScrollViewComponent; - -export class BottomSheetScrollViewType extends BottomSheetScrollViewBase { - /** - * Scrolls to a given x, y offset, either immediately or with a smooth animation. - * Syntax: - * - * scrollTo(options: {x: number = 0; y: number = 0; animated: boolean = true}) - * - * Note: The weird argument signature is due to the fact that, for historical reasons, - * the function also accepts separate arguments as an alternative to the options object. - * This is deprecated due to ambiguity (y before x), and SHOULD NOT BE USED. - */ - scrollTo( - y?: number | { x?: number; y?: number; animated?: boolean }, - x?: number, - animated?: boolean - ): void; - - /** - * A helper function that scrolls to the end of the scrollview; - * If this is a vertical ScrollView, it scrolls to the bottom. - * If this is a horizontal ScrollView scrolls to the right. - * - * The options object has an animated prop, that enables the scrolling animation or not. - * The animated prop defaults to true - */ - scrollToEnd(options?: { animated: boolean }): void; - - /** - * Returns a reference to the underlying scroll responder, which supports - * operations like `scrollTo`. All ScrollView-like components should - * implement this method so that they can be composed while providing access - * to the underlying scroll responder's methods. - */ - getScrollResponder(): JSX.Element; - - getScrollableNode(): any; - - // Undocumented - getInnerViewNode(): any; - - /** - * @deprecated Use scrollTo instead - */ - scrollWithoutAnimationTo?: (y: number, x: number) => void; -} diff --git a/src/components/sectionList/SectionList.tsx b/src/components/sectionList/SectionList.tsx deleted file mode 100644 index b0d1d133b..000000000 --- a/src/components/sectionList/SectionList.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React, { - useImperativeHandle, - useEffect, - useRef, - Ref, - forwardRef, - memo, -} from 'react'; -import { - SectionList as RNSectionList, - SectionListProps as RNSectionListProps, -} from 'react-native'; -import Animated from 'react-native-reanimated'; -import { NativeViewGestureHandler } from 'react-native-gesture-handler'; -import BottomSheetDraggableView from '../bottomSheetDraggableView'; -import { useScrollableInternal, useBottomSheetInternal } from '../../hooks'; -import type { - BottomSheetSectionListProps, - BottomSheetSectionListType, -} from './types'; -import { styles } from './styles'; - -const AnimatedSectionList = Animated.createAnimatedComponent< - RNSectionListProps ->(RNSectionList); - -const BottomSheetSectionListName = 'SectionList'; - -const BottomSheetSectionListComponent = forwardRef( - (props: BottomSheetSectionListProps, ref: Ref) => { - // props - const { - focusHook: useFocusHook = useEffect, - overScrollMode = 'never', - ...rest - } = props; - - // refs - const nativeGestureRef = useRef(null); - - // hooks - const { - scrollableRef, - scrollableAnimatedProps, - handleScrollEvent, - handleSettingScrollable, - } = useScrollableInternal(BottomSheetSectionListName); - const { enableContentPanningGesture } = useBottomSheetInternal(); - - // effects - // @ts-ignore - useImperativeHandle(ref, () => scrollableRef.current); - useFocusHook(handleSettingScrollable); - - // render - return ( - - - - - - ); - } -); - -const BottomSheetSectionList = memo(BottomSheetSectionListComponent); -BottomSheetSectionList.displayName = 'BottomSheetSectionList'; - -export default (BottomSheetSectionList as any) as typeof BottomSheetSectionListType; diff --git a/src/components/sectionList/index.ts b/src/components/sectionList/index.ts deleted file mode 100644 index f6a374b63..000000000 --- a/src/components/sectionList/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './SectionList'; diff --git a/src/components/sectionList/styles.ts b/src/components/sectionList/styles.ts deleted file mode 100644 index 31e937d84..000000000 --- a/src/components/sectionList/styles.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { StyleSheet } from 'react-native'; - -export const styles = StyleSheet.create({ - container: { - flex: 1, - }, -}); diff --git a/src/components/sectionList/types.d.ts b/src/components/sectionList/types.d.ts deleted file mode 100644 index 82a15f013..000000000 --- a/src/components/sectionList/types.d.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { Component, EffectCallback, DependencyList } from 'react'; -import type { SectionListProps as RNSectionListProps } from 'react-native'; - -type BottomSheetSectionListProps = Omit< - RNSectionListProps, - 'decelerationRate' | 'onScrollBeginDrag' | 'scrollEventThrottle' -> & { - /** - * This needed when bottom sheet used with multiple scrollables to allow bottom sheet detect the current scrollable ref, especially when used with `React Navigation`. You will need to provide `useFocusEffect` from `@react-navigation/native`. - * @type (effect: EffectCallback, deps?: DependencyList) => void - */ - focusHook?: (effect: EffectCallback, deps?: DependencyList) => void; -}; - -export class BottomSheetSectionListType extends Component< - BottomSheetSectionListProps -> { - /** - * Scrolls to the item at the specified sectionIndex and itemIndex (within the section) - * positioned in the viewable area such that viewPosition 0 places it at the top - * (and may be covered by a sticky header), 1 at the bottom, and 0.5 centered in the middle. - */ - scrollToLocation(params: SectionListScrollParams): void; - - /** - * Tells the list an interaction has occurred, which should trigger viewability calculations, e.g. - * if `waitForInteractions` is true and the user has not scrolled. This is typically called by - * taps on items or by navigation actions. - */ - recordInteraction(): void; - - /** - * Displays the scroll indicators momentarily. - * - * @platform ios - */ - flashScrollIndicators(): void; - - /** - * Provides a handle to the underlying scroll responder. - */ - getScrollResponder(): ScrollView | undefined; - - /** - * Provides a handle to the underlying scroll node. - */ - getScrollableNode(): NodeHandle | undefined; -} diff --git a/src/hooks/useScrollable.ts b/src/hooks/useScrollable.ts index fe71f4a71..9fddb0bdf 100644 --- a/src/hooks/useScrollable.ts +++ b/src/hooks/useScrollable.ts @@ -54,11 +54,10 @@ export const useScrollable = () => { }, []); const flashScrollableIndicators = useCallback(() => { - let type = scrollableRef.current?.type ?? undefined; let node = scrollableRef.current?.node ?? undefined; let didResize = scrollableRef.current?.didResize ?? false; - if (!type || !node) { + if (!node) { return; } diff --git a/src/hooks/useScrollableInternal.ts b/src/hooks/useScrollableInternal.ts index 08fe460f6..01acfa99a 100644 --- a/src/hooks/useScrollableInternal.ts +++ b/src/hooks/useScrollableInternal.ts @@ -9,14 +9,14 @@ import { useAnimatedProps, } from 'react-native-reanimated'; import { useBottomSheetInternal } from './useBottomSheetInternal'; -import type { Scrollable, ScrollableType } from '../types'; +import type { Scrollable } from '../types'; import { ANIMATION_STATE, SCROLLABLE_DECELERATION_RATE_MAPPER, SCROLLABLE_STATE, } from '../constants'; -export const useScrollableInternal = (type: ScrollableType) => { +export const useScrollableInternal = () => { // refs const scrollableRef = useAnimatedRef(); const scrollableContentOffsetY = useSharedValue(0); @@ -101,7 +101,6 @@ export const useScrollableInternal = (type: ScrollableType) => { if (id) { setScrollableRef({ id: id, - type, node: scrollableRef, didResize: false, }); @@ -118,7 +117,6 @@ export const useScrollableInternal = (type: ScrollableType) => { scrollableContentOffsetY, scrollableRef, setScrollableRef, - type, ]); return { diff --git a/src/index.ts b/src/index.ts index 99554cf75..67023c41f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,42 +1,49 @@ +// bottom sheet export { default } from './components/bottomSheet'; -// scrollables -export { default as BottomSheetFlatList } from './components/flatList'; -export { default as BottomSheetSectionList } from './components/sectionList'; -export { default as BottomSheetScrollView } from './components/scrollView'; +// bottom sheet modal +export { default as BottomSheetModal } from './components/bottomSheetModal'; +export { default as BottomSheetModalProvider } from './components/bottomSheetModalProvider'; + +//#region hooks +export { useBottomSheet } from './hooks/useBottomSheet'; +export { useBottomSheetModal } from './hooks/useBottomSheetModal'; +export { useBottomSheetSpringConfigs } from './hooks/useBottomSheetSpringConfigs'; +export { useBottomSheetTimingConfigs } from './hooks/useBottomSheetTimingConfigs'; +//#endregion + +//#region components +export { + BottomSheetScrollView, + BottomSheetSectionList, + BottomSheetFlatList, +} from './components/bottomSheetScrollable'; export { default as BottomSheetDraggableView } from './components/bottomSheetDraggableView'; export { default as BottomSheetView } from './components/view'; +export { default as BottomSheetTextInput } from './components/bottomSheetTextInput'; +export { default as BottomSheetBackdrop } from './components/bottomSheetBackdrop'; -// helpers import BottomSheetTouchable from './components/touchables'; export const { TouchableHighlight, TouchableOpacity, TouchableWithoutFeedback, } = BottomSheetTouchable; +//#endregion -// text input -export { default as BottomSheetTextInput } from './components/bottomSheetTextInput'; - -// backdrop -export { default as BottomSheetBackdrop } from './components/bottomSheetBackdrop'; - -// modal -export { default as BottomSheetModal } from './components/bottomSheetModal'; -export { default as BottomSheetModalProvider } from './components/bottomSheetModalProvider'; - -// hooks -export { useBottomSheet } from './hooks/useBottomSheet'; -export { useBottomSheetModal } from './hooks/useBottomSheetModal'; -export { useBottomSheetSpringConfigs } from './hooks/useBottomSheetSpringConfigs'; -export { useBottomSheetTimingConfigs } from './hooks/useBottomSheetTimingConfigs'; - -// components types +//#region types export type { BottomSheetProps } from './components/bottomSheet'; export type { BottomSheetModalProps } from './components/bottomSheetModal'; export type { BottomSheetHandleProps } from './components/bottomSheetHandle'; export type { BottomSheetBackgroundProps } from './components/bottomSheetBackground'; export type { BottomSheetBackdropProps } from './components/bottomSheetBackdrop'; +export type { + BottomSheetFlatListMethods, + BottomSheetScrollViewMethods, + BottomSheetSectionListMethods, +} from './components/bottomSheetScrollable'; +//#endregion + // logger export { enableLogging } from './utilities/logger'; diff --git a/src/types.d.ts b/src/types.d.ts index ded482ad9..40088ab4f 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -74,7 +74,6 @@ export type ScrollableType = 'FlatList' | 'ScrollView' | 'SectionList' | 'View'; export type ScrollableRef = { id: number; node: React.RefObject; - type: ScrollableType; didResize: boolean; }; //#endregion diff --git a/yarn.lock b/yarn.lock index 480f02b7a..5c3d2833d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2223,10 +2223,10 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== -"@types/react-native@^0.63.8": - version "0.63.8" - resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.63.8.tgz#73ec087122c64c309eeaf150b565b8d755f0fb1f" - integrity sha512-QRwGFRTyGafRVTUS+0GYyJrlpmS3boyBaFI0ULSc+mh/lQNxrzbdQvoL2k5X7+t9hxyqA4dTQAlP6l0ir/fNJQ== +"@types/react-native@^0.64.5": + version "0.64.5" + resolved "https://registry.yarnpkg.com/@types/react-native/-/react-native-0.64.5.tgz#219738b52b2e372ec057d3c8f20fbd6c37b245cd" + integrity sha512-k0r8MnQX7UFboZDvMKLov26gFLXKrNgLhCfSVhjaZ6wMUofKijxvee7/wgfAqtT2zS5FR4an4+qn0r72SCbw3g== dependencies: "@types/react" "*" @@ -2238,12 +2238,13 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/react@^16.9.46": - version "16.9.46" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.46.tgz#f0326cd7adceda74148baa9bff6e918632f5069e" - integrity sha512-dbHzO3aAq1lB3jRQuNpuZ/mnu+CdD3H0WVaaBQA8LTT3S33xhVBUj232T8M3tAhSWJs/D/UqORYUlJNl/8VQZg== +"@types/react@^17.0.5": + version "17.0.5" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.5.tgz#3d887570c4489011f75a3fc8f965bf87d09a1bea" + integrity sha512-bj4biDB9ZJmGAYTWSKJly6bMr4BLUiBrx9ujiJEoP9XIDY9CTaPGxE5QWN/1WjpPLzYF7/jRNnV2nNxNe970sw== dependencies: "@types/prop-types" "*" + "@types/scheduler" "*" csstype "^3.0.2" "@types/responselike@*", "@types/responselike@^1.0.0": @@ -2253,6 +2254,11 @@ dependencies: "@types/node" "*" +"@types/scheduler@*": + version "0.16.1" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275" + integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA== + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"