Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite library, unlimited number of sections, configurable animations, extensible, no magic numbers, typescript #9

Open
Aryk opened this issue Feb 8, 2022 · 0 comments

Comments

@Aryk
Copy link

Aryk commented Feb 8, 2022

I rewrote this library to make it more flexible. If one wanted to add X number of switches, that would be pretty easy to do.

Take a look!

import React, {useEffect, useRef, useState} from "react";
import {
  Animated,
  Dimensions,
  PanResponder,
  View,
  StyleProp,
  ViewStyle,
  EasingFunction,
} from "react-native";

interface ISwitchComponent {
  currentIndex?: number;
  animatedValue?: Animated.AnimatedInterpolation;
}

// Taken from here: https://github.com/victorkvarghese/rn-slider-switch
// And rewritten to follow functional components but also to allow to overwrite all of the hardcoded things.
interface IMultiSwitch {
  currentIndex: number;
  animate?: boolean;
  duration?: number;
  easing?: EasingFunction;
  width?: number;
  height?: number;
  switcherWidth?: number,
  borderWidth?: number,
  disableScroll?: (bool: boolean) => any;
  onIndexChanged?: (index: number) => any;
  disableSwitch?: boolean;
  containerStyle?: StyleProp<ViewStyle>;
  tabStyle?: StyleProp<ViewStyle>;
  useNativeDriver?: boolean;
  children?: any[];
  SwitchComponent: React.ComponentType<ISwitchComponent>;
  MultiSwitchHeader?: JSX.Element;
  MultiSwitchFooter?: JSX.Element;
}
const MultiSwitch = ({
  children,
  currentIndex,
  animate = true,
  duration = 100,
  easing,
  width = Dimensions.get("window").width - 30,
  height = 55,
  borderWidth = 1,
  switcherWidth = width / children.length,
  disableScroll,
  onIndexChanged,
  disableSwitch,
  containerStyle,
  tabStyle,
  useNativeDriver = true,
  SwitchComponent,
  MultiSwitchHeader,
  MultiSwitchFooter,
}: IMultiSwitch) => {
  const [posValue, setPosValue] = useState(0);
  const isParentScrollDisabledRef = useRef(false);
  const position = useRef(new Animated.Value(0)).current;
  const currentAnimationRef = useRef<Animated.CompositeAnimation>();

  const animateToIndex = (index: number) => {
    if (disableSwitch) return;
    currentAnimationRef.current?.stop(); // stop previous animation if was running

    const toValue = index * switcherWidth;
    if (animate) {
      currentAnimationRef.current = Animated.timing(position, {toValue, duration, easing, useNativeDriver});
      currentAnimationRef.current.start(() => setPosValue(toValue));
    } else {
      position.setValue(toValue);
      setPosValue(toValue)
    }

    onIndexChanged?.(index);
  };

  const animatedValue = position.interpolate({
    inputRange: [0, width - switcherWidth],
    outputRange: [0, 1],
  });

  useEffect(() => { animateToIndex(currentIndex); }, [currentIndex]);

  const panResponder = PanResponder.create({
    onStartShouldSetPanResponder: () => true,
    onStartShouldSetPanResponderCapture: () => true,
    onMoveShouldSetPanResponder: () => true,
    onMoveShouldSetPanResponderCapture: () => true,

    onPanResponderGrant: () => {
      // disable parent scroll if slider is inside a scrollview
      if (!isParentScrollDisabledRef.current) {
        disableScroll?.(true);
        isParentScrollDisabledRef.current = true;
      }
    },

    onPanResponderMove: (evt, gestureState) => {
      if (!disableSwitch) {
        let finalValue = gestureState.dx + posValue;
        if (finalValue >= 0 && finalValue <= (width - switcherWidth))
          position.setValue(posValue + gestureState.dx);
      }
    },

    onPanResponderTerminationRequest: () => true,

    onPanResponderRelease: (evt, gestureState) => {
      if (!disableSwitch) {
        let finalValue = gestureState.dx + posValue;
        isParentScrollDisabledRef.current = false;
        disableScroll?.(false);
        animateToIndex(Math.round(finalValue / switcherWidth));
      }
    },

    onPanResponderTerminate: () => {},
    // Returns whether this component should block native components from becoming the JS
    // responder. Returns true by default. Is currently only supported on android.
    onShouldBlockNativeResponder: () => true,
  });

  const selectorHeight = height - 2 * borderWidth;

  return <View style={[
    {
      width,
      height,
      flexDirection: "row",
      backgroundColor: "#efefef",
      alignItems: "center",
      justifyContent: "center",
      borderWidth,
      borderColor: "#efefef",
      overflow: "hidden",
      borderRadius: height / 2,
    },
    containerStyle,
  ]}>
    {MultiSwitchHeader}
    {children}
    <Animated.View
      {...panResponder.panHandlers}
      style={[
        {
          flexDirection: "row",
          position: "absolute",
          top: 0,
          left: 0,
          backgroundColor: "#FFF",
          borderRadius: selectorHeight / 2,
          height: selectorHeight,
          alignItems: "center",
          justifyContent: "center",
          width: switcherWidth,
          elevation: 4,
          shadowOpacity: 0.31,
          shadowRadius: 10,
          shadowColor: "#A69E9E",
          transform: [{ translateX: position }]
        },
        tabStyle,
      ]}
    >
      <SwitchComponent {...{animatedValue, currentIndex}} />
    </Animated.View>
    {MultiSwitchFooter}
  </View>
};

export {
  ISwitchComponent,
  IMultiSwitch,
  MultiSwitch,
}
@Aryk Aryk changed the title Typescript Rewrite Rewrite library, X number of tabs, native animations, extensible, no magic numbers, typescript Feb 9, 2022
@Aryk Aryk changed the title Rewrite library, X number of tabs, native animations, extensible, no magic numbers, typescript Rewrite library, unlimited number of sections, configurable animations, extensible, no magic numbers, typescript Feb 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant