PedroBern / react-native-collapsible-tab-view

A cross-platform Collapsible Tab View component for React Native
MIT License
829 stars 160 forks source link

Feat/header touchables and horizontal scroll #254

Open tiagocorreiaalmeida opened 2 years ago

tiagocorreiaalmeida commented 2 years ago

The idea of the PR is to allow the header to contain Touchable elements and horizontal views that don't affect the vertical scroll behaviour of the tab view, this means we don't have to manually add pointerEvents to all views that are not target of the touch events, it also means we can have both the behaviours, having the element being target of a touch event as press and still be able to scroll vertically on the same element.

Improvements that need to be made:

Also, as mentioned on the feature request, I'm not sure if I missed any of the project internals that could introduce new bugs.

https://user-images.githubusercontent.com/17684951/166218485-9ae0f41b-7b44-45f0-8ff8-ab28a15aa719.mp4

tiagocorreiaalmeida commented 2 years ago

Hey @andreialecu @PedroBern, any update on this? If the approach doesn't feel the best taking into consideration the project design or if there's anything else that can be done to improve it, let me know

andreialecu commented 2 years ago

This doesn't currently handle gliding when the finger is released, correct?

I did think about this implementation in the past, but it felt a bit hacky because of missing gliding and momentum support.

I wonder if we can instead provide some extension points that allows users to handle this in their own app instead of in the library.

tiagocorreiaalmeida commented 2 years ago

@andreialecu Is the discord still up? Maybe we can go trough it there

Synapse96 commented 1 year ago

any chance this PR would be resolved soon? Would love to have this working as well

tobzy commented 1 year ago

Please can this be merged soon? I'm looking forward to it.

DSKonstantin commented 1 year ago

Please merge this.

DreamakerDimas commented 1 year ago

Hello @andreialecu. Issue with touchable will be fixed soon or currently this PR changes is the best what I can do to make it work in my project?

deflexable commented 1 year ago

@andreialecu it almost a year and would love if this PR could be merge

andreialecu commented 1 year ago

As the Author mentioned, the feature is currently incomplete and has some issues that need to be addressed before it can be merged.

Additionally, I have concerns about adding this feature directly to the library due to the potential complexity it could introduce. However, I am open to exploring alternative solutions, such as implementing an extension point that would allow users to easily add this functionality themselves.

If any of you are still interested in pursuing this feature, I would appreciate it if you could help address the issues with the current pull request and explore the possibility of an extension point.

tiagocorreiaalmeida commented 1 year ago

@andreialecu Hey, I actually made a fork of the project and adapt it to allow scroll on header content on the project I'm working, I did remove things that I didn't need, like for instance snap points, but maybe if it makes sense I can try to take some time to review what I did to see if it can be improved and implemented without directly affecting the existent code base (like an extension as you mentioned). Is the project discord still up? Might be easier to discuss some internals, also noticed you are once again maintaining the project, thank you for that 👍

HananoshikaYomaru commented 1 year ago

Although this is not fully implemented, this feature is quite important and highly requested. I think should accept a working approach first, warn the users of known limitation and then refine it afterwards.

nggonzalez commented 11 months ago

Also needed this feature and this PR was helpful in unblocking the use of this library. I tried pointerEvents=box-none, but couldn't get it working. I can't use pointerEvents=none because the majority of my headers' contents are touchable.

To help others that may stumble on this PR, here's a patch I created using patch-package that uses some of this code to get header scrolling working on both Android and iOS. The patch is created on v6.1.4. Thank you @tiagocorreiaalmeida for the initial PR and work that this patch is based on!

I understand that PanGestureHandler isn't perfect, but it would be helpful to make it easier to add header scroll. Exposing the scroll sync function and the scrollY might be sufficient to get started so this code can live in user land.

react-native-collapsible-tab-view+6.1.4.patch

ahmetkuslular commented 10 months ago

@andreialecu hi. It seems to have been a long time since this PR. The problem still persists. Is there any work on this?

deflexable commented 10 months ago

@ahmetkuslular you can try out this work around https://www.npmjs.com/package/react-native-tab-view-header

ahmetkuslular commented 10 months ago

@ahmetkuslular you can try out this work around https://www.npmjs.com/package/react-native-tab-view-header

Although this package is successful in header, it does not meet my requests in general. Is it possible to import the properly working header component here into this project?

nggonzalez commented 10 months ago

@ahmetkuslular did you try the patch above? It should help unblock you

ahmetkuslular commented 10 months ago

@ahmetkuslular did you try the patch above? It should help unblock you

It's not patch, but I pulled these codes to my locale and tried them together with the current codes. It works seamlessly. I don't know if there are cases that need attention here.

senghuotlay commented 9 months ago

For anyone who wanted to code snippet without needing patch version for 6.21

@nggonzalez one thing about this is that although we're able to scroll, the scroll with snap stopped working, because it seems like we dont have enough velocity?

const isSlidingTopContainer = useSharedValue(false)
      const isSlidingTopContainerPrev = useSharedValue(false)
      const isTopContainerOutOfSync = useSharedValue(false)

 const panGestureScrollYCtx = useSharedValue(0)
      const panGesture = useMemo(
        () =>
          Gesture.Pan()
            .activeOffsetY([-10, 10])
            .onUpdate((event) => {
              if (!isSlidingTopContainer.value) {
                panGestureScrollYCtx.value = scrollYCurrent.value
                isSlidingTopContainer.value = true
                return
              }

              scrollYCurrent.value = interpolate(
                -event.translationY + panGestureScrollYCtx.value,
                [0, headerScrollDistance.value],
                [0, headerScrollDistance.value],
                Extrapolate.CLAMP
              )
            })
            .onEnd((event) => {
              if (!isSlidingTopContainer.value) return

              panGestureScrollYCtx.value = 0
              scrollYCurrent.value = withDecay(
                {
                  velocity: -event.velocityY,
                  clamp: [0, headerScrollDistance.value],
                  deceleration: IS_IOS ? 0.998 : 0.99,
                },
                (finished) => {
                  isSlidingTopContainer.value = false
                  isTopContainerOutOfSync.value = !!finished
                }
              )
            }),
        [
          headerScrollDistance.value,
          isSlidingTopContainer,
          isTopContainerOutOfSync,
          panGestureScrollYCtx,
          scrollYCurrent,
        ]
      )

      useAnimatedReaction(
        () => scrollYCurrent.value - contentInset.value,
        (nextPosition, previousPosition) => {
          if (
            nextPosition !== previousPosition &&
            isSlidingTopContainer.value
          ) {
            resyncTabScroll()
          }
        }
      )

      /* Syncs the scroll of the active tab once we complete the scroll gesture
      on the header and the decay animation completes with success
       */
      useAnimatedReaction(
        () => {
          return (
            isSlidingTopContainer.value !== isSlidingTopContainerPrev.value &&
            isTopContainerOutOfSync.value
          )
        },
        (result) => {
          isSlidingTopContainerPrev.value = isSlidingTopContainer.value

          if (!result) return
          if (isSlidingTopContainer.value === true) return

          resyncTabScroll()

          isTopContainerOutOfSync.value = false
        }
      )
    <GestureDetector gesture={panGesture}>
              <Animated.View
                pointerEvents="box-none"
                style={[
                  styles.topContainer,
                  headerContainerStyle,
                  !cancelTranslation && stylez,
                ]}
              >
                <View
                  style={[styles.container, styles.headerContainer]}
                  onLayout={getHeaderHeight}
                  pointerEvents="box-none"
                >
                  {renderHeader &&
                    renderHeader({
                      containerRef,
                      index,
                      tabNames: tabNamesArray,
                      focusedTab,
                      indexDecimal,
                      onTabPress,
                      tabProps,
                    })}
                </View>
                <View
                  style={[styles.container, styles.tabBarContainer]}
                  onLayout={getTabBarHeight}
                  pointerEvents="box-none"
                >
                  {renderTabBar &&
                    renderTabBar({
                      containerRef,
                      index,
                      tabNames: tabNamesArray,
                      focusedTab,
                      indexDecimal,
                      width,
                      onTabPress,
                      tabProps,
                    })}
                </View>
              </Animated.View>
            </GestureDetector>
AugustoAleGon commented 9 months ago

@andreialecu is this something on the roadmap? 👀 This feature is needed.

bpeck81 commented 6 months ago

Also needed this feature and this PR was helpful in unblocking the use of this library. I tried pointerEvents=box-none, but couldn't get it working. I can't use pointerEvents=none because the majority of my headers' contents are touchable.

To help others that may stumble on this PR, here's a patch I created using patch-package that uses some of this code to get header scrolling working on both Android and iOS. The patch is created on v6.1.4. Thank you @tiagocorreiaalmeida for the initial PR and work that this patch is based on!

I understand that PanGestureHandler isn't perfect, but it would be helpful to make it easier to add header scroll. Exposing the scroll sync function and the scrollY might be sufficient to get started so this code can live in user land.

react-native-collapsible-tab-view+6.1.4.patch

The patch didn't work for me unfortunately.