react-native#I18nManager TypeScript Examples

The following examples show how to use react-native#I18nManager. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: utils.ts    From react-native-range-slider-expo with MIT License 6 votes vote down vote up
osRtl = I18nManager.isRTL
Example #2
Source File: DatePicker.tsx    From react-native-jigsaw with MIT License 6 votes vote down vote up
styles = StyleSheet.create({
  container: {
    alignSelf: "stretch",
  },
  picker: {
    position: "absolute",
    bottom: 0,
    left: 0,
    right: 0,
    flexDirection: "row",
    justifyContent: "center",
  },
  underline: {
    position: "absolute",
    left: 0,
    right: 0,
    bottom: 0,
    height: 2,
  },
  input: {
    flexGrow: 1,
    justifyContent: "center",
    textAlignVertical: "center",
    margin: 0,
    textAlign: I18nManager.isRTL ? "right" : "left",
  },
  placeholder: {
    position: "absolute",
    left: 0,
  },
  pickerContainer: { flexDirection: "column", width: "100%", zIndex: 100 },
  closeButton: {
    alignSelf: "flex-end",
  },
})
Example #3
Source File: Text.tsx    From react-native-jigsaw with MIT License 6 votes vote down vote up
render() {
    const { style, ...rest } = this.props;
    const writingDirection = I18nManager.isRTL ? "rtl" : "ltr";

    return (
      <NativeText
        {...rest}
        ref={(c) => {
          this._root = c;
        }}
        style={[
          {
            textAlign: "left",
            writingDirection,
          },
          style,
        ]}
      />
    );
  }
Example #4
Source File: TextField.tsx    From react-native-jigsaw with MIT License 6 votes vote down vote up
styles = StyleSheet.create({
  container: {
    alignSelf: "stretch",
  },
  placeholder: {
    position: "absolute",
    left: 0,
  },
  underline: {
    position: "absolute",
    left: 0,
    right: 0,
    bottom: 0,
    height: 2,
  },
  input: {
    flexGrow: 1,
    justifyContent: "center",
    textAlignVertical: "center",
    margin: 0,
    textAlign: I18nManager.isRTL ? "right" : "left",
  },
})
Example #5
Source File: i18n.ts    From nyxo-app with GNU General Public License v3.0 6 votes vote down vote up
setI18nConfig = (): void => {
  // fallback if no available language fits
  const fallback = { languageTag: 'en', isRTL: false }

  const { languageTag, isRTL } =
    findBestAvailableLanguage(Object.keys(translationGetters)) || fallback

  // clear translation cache
  if (translate?.cache.clear) translate.cache.clear()
  // update layout direction
  I18nManager.forceRTL(isRTL)

  // set i18n-js config
  I18n.translations = { [languageTag]: translationGetters[languageTag]() }
  I18n.locale = languageTag
}
Example #6
Source File: HeaderStyleInterpolators.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
/**
 * Simple translate animation to translate the header to left.
 */
export function forSlideLeft({
  current,
  next,
  layouts: { screen },
}: StackHeaderInterpolationProps): StackHeaderInterpolatedStyle {
  const progress = add(
    current.progress.interpolate({
      inputRange: [0, 1],
      outputRange: [0, 1],
      extrapolate: 'clamp',
    }),
    next
      ? next.progress.interpolate({
          inputRange: [0, 1],
          outputRange: [0, 1],
          extrapolate: 'clamp',
        })
      : 0
  );

  const translateX = progress.interpolate({
    inputRange: [0, 1, 2],
    outputRange: I18nManager.isRTL
      ? [-screen.width, 0, screen.width]
      : [screen.width, 0, -screen.width],
  });

  const transform = [{ translateX }];

  return {
    leftButtonStyle: { transform },
    rightButtonStyle: { transform },
    titleStyle: { transform },
    backgroundStyle: { transform },
  };
}
Example #7
Source File: HeaderStyleInterpolators.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
/**
 * Simple translate animation to translate the header to right.
 */
export function forSlideRight({
  current,
  next,
  layouts: { screen },
}: StackHeaderInterpolationProps): StackHeaderInterpolatedStyle {
  const progress = add(
    current.progress.interpolate({
      inputRange: [0, 1],
      outputRange: [0, 1],
      extrapolate: 'clamp',
    }),
    next
      ? next.progress.interpolate({
          inputRange: [0, 1],
          outputRange: [0, 1],
          extrapolate: 'clamp',
        })
      : 0
  );

  const translateX = progress.interpolate({
    inputRange: [0, 1, 2],
    outputRange: I18nManager.isRTL
      ? [screen.width, 0, -screen.width]
      : [-screen.width, 0, screen.width],
  });

  const transform = [{ translateX }];

  return {
    leftButtonStyle: { transform },
    rightButtonStyle: { transform },
    titleStyle: { transform },
    backgroundStyle: { transform },
  };
}
Example #8
Source File: getInvertedMultiplier.tsx    From nlw2-proffy with MIT License 6 votes vote down vote up
export default function getInvertedMultiplier(
  gestureDirection: GestureDirection
): 1 | -1 {
  switch (gestureDirection) {
    case 'vertical':
      return 1;
    case 'vertical-inverted':
      return -1;
    case 'horizontal':
      return I18nManager.isRTL ? -1 : 1;
    case 'horizontal-inverted':
      return I18nManager.isRTL ? 1 : -1;
  }
}
Example #9
Source File: App.tsx    From sellflow with MIT License 5 votes vote down vote up
I18nManager.forceRTL(CustomTheme.isRTL);
Example #10
Source File: Slider.tsx    From react-native-range-slider-expo with MIT License 5 votes vote down vote up
osRtl = I18nManager.isRTL
Example #11
Source File: TextSlider.tsx    From react-native-range-slider-expo with MIT License 5 votes vote down vote up
osRtl = I18nManager.isRTL
Example #12
Source File: ProgressBar.tsx    From react-native-jigsaw with MIT License 5 votes vote down vote up
render() {
    const {
      borderColor,
      borderRadius = 4,
      borderWidth = 1,
      children,
      color = "rgba(0, 122, 255, 1)",
      style,
      unfilledColor,
      width = 150,
      ...restProps
    } = this.props;

    const innerWidth = Math.max(0, width || this.state.width) - borderWidth * 2;
    const containerStyle: StyleProp<ViewStyle> = {
      width,
      borderWidth,
      borderColor: borderColor || color,
      borderRadius,
      overflow: "hidden",
      backgroundColor: unfilledColor,
    };
    const progressStyle = {
      backgroundColor: color,
      // Always take up full height of container.
      height: "100%",
      transform: [
        {
          translateX: this.state.animationValue.interpolate({
            inputRange: [0, 1],
            outputRange: [innerWidth * -INDETERMINATE_WIDTH_FACTOR, innerWidth],
          }),
        },
        {
          translateX: this.state.progress.interpolate({
            inputRange: [0, 1],
            outputRange: [innerWidth / (I18nManager.isRTL ? 2 : -2), 0],
          }),
        },
        {
          // Interpolation a temp workaround for https://github.com/facebook/react-native/issues/6278
          scaleX: this.state.progress.interpolate({
            inputRange: [0, 1],
            outputRange: [0.0001, 1],
          }),
        },
      ],
    };

    return (
      <View
        style={[containerStyle, style]}
        onLayout={this.handleLayout}
        {...restProps}
      >
        <Animated.View style={progressStyle} />
        {children}
      </View>
    );
  }
Example #13
Source File: HeaderBackButton.tsx    From nlw2-proffy with MIT License 5 votes vote down vote up
styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    flexDirection: 'row',
    ...Platform.select({
      ios: null,
      default: {
        marginVertical: 3,
        marginHorizontal: 11,
      },
    }),
  },
  disabled: {
    opacity: 0.5,
  },
  label: {
    fontSize: 17,
    // Title and back label are a bit different width due to title being bold
    // Adjusting the letterSpacing makes them coincide better
    letterSpacing: 0.35,
  },
  labelWrapper: {
    // These styles will make sure that the label doesn't fill the available space
    // Otherwise it messes with the measurement of the label
    flexDirection: 'row',
    alignItems: 'flex-start',
  },
  icon: Platform.select({
    ios: {
      height: 21,
      width: 13,
      marginLeft: 8,
      marginRight: 22,
      marginVertical: 12,
      resizeMode: 'contain',
      transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
    },
    default: {
      height: 24,
      width: 24,
      margin: 3,
      resizeMode: 'contain',
      transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
    },
  }),
  iconWithLabel:
    Platform.OS === 'ios'
      ? {
          marginRight: 6,
        }
      : {},
  iconMaskContainer: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'center',
  },
  iconMaskFillerRect: {
    flex: 1,
    backgroundColor: '#000',
  },
  iconMask: {
    height: 21,
    width: 13,
    marginLeft: -14.5,
    marginVertical: 12,
    alignSelf: 'center',
    resizeMode: 'contain',
    transform: [{ scaleX: I18nManager.isRTL ? -1 : 1 }],
  },
})
Example #14
Source File: BackgroundCircle.tsx    From react-native-paper-onboarding with MIT License 5 votes vote down vote up
BackgroundCircleComponent = ({
  index,
  animatedIndex,
  color,
  extendedSize,
  bottomPosition,
  screenDimensions,
  indicatorSize,
  animatedIndicatorsContainerPosition,
}: BackgroundCircleProps) => {
  //#region variables
  //#endregion

  //#region animations
  const animatedFocus = useMemo(
    () =>
      interpolate(animatedIndex, {
        inputRange: [index - 1, index, index + 1],
        outputRange: [0, 1, 2],
        extrapolate: Extrapolate.CLAMP,
      }),
    [animatedIndex, index]
  );
  const animatedRadius = useMemo(
    () =>
      interpolate(animatedFocus, {
        inputRange: [0, 1],
        outputRange: [0, extendedSize],
        extrapolate: Extrapolate.CLAMP,
      }),
    [animatedFocus, extendedSize]
  );
  const animatedLeftPosition = useMemo(
    () =>
      add(
        animatedIndicatorsContainerPosition,
        indicatorSize / 2,
        I18nManager.isRTL
          ? -((index + 1) * indicatorSize)
          : index * indicatorSize,
        I18nManager.isRTL ? screenDimensions.width : 0
      ),
    [
      animatedIndicatorsContainerPosition,
      index,
      indicatorSize,
      screenDimensions.width,
    ]
  );
  //#endregion

  // render
  return (
    <AnimatedCircle
      r={animatedRadius}
      cy={bottomPosition}
      cx={animatedLeftPosition}
      fill={color}
    />
  );
}
Example #15
Source File: HeaderStyleInterpolators.tsx    From nlw2-proffy with MIT License 4 votes vote down vote up
/**
 * Standard UIKit style animation for the header where the title fades into the back button label.
 */
export function forUIKit({
  current,
  next,
  layouts,
}: StackHeaderInterpolationProps): StackHeaderInterpolatedStyle {
  const defaultOffset = 100;
  const leftSpacing = 27;

  // The title and back button title should cross-fade to each other
  // When screen is fully open, the title should be in center, and back title should be on left
  // When screen is closing, the previous title will animate to back title's position
  // And back title will animate to title's position
  // We achieve this by calculating the offsets needed to translate title to back title's position and vice-versa
  const leftLabelOffset = layouts.leftLabel
    ? (layouts.screen.width - layouts.leftLabel.width) / 2 - leftSpacing
    : defaultOffset;
  const titleLeftOffset = layouts.title
    ? (layouts.screen.width - layouts.title.width) / 2 - leftSpacing
    : defaultOffset;

  // When the current title is animating to right, it is centered in the right half of screen in middle of transition
  // The back title also animates in from this position
  const rightOffset = layouts.screen.width / 4;

  const progress = add(
    current.progress.interpolate({
      inputRange: [0, 1],
      outputRange: [0, 1],
      extrapolate: 'clamp',
    }),
    next
      ? next.progress.interpolate({
          inputRange: [0, 1],
          outputRange: [0, 1],
          extrapolate: 'clamp',
        })
      : 0
  );

  return {
    leftButtonStyle: {
      opacity: progress.interpolate({
        inputRange: [0.3, 1, 1.5],
        outputRange: [0, 1, 0],
      }),
    },
    leftLabelStyle: {
      transform: [
        {
          translateX: progress.interpolate({
            inputRange: [0, 1, 2],
            outputRange: I18nManager.isRTL
              ? [-rightOffset, 0, leftLabelOffset]
              : [leftLabelOffset, 0, -rightOffset],
          }),
        },
      ],
    },
    rightButtonStyle: {
      opacity: progress.interpolate({
        inputRange: [0.3, 1, 1.5],
        outputRange: [0, 1, 0],
      }),
    },
    titleStyle: {
      opacity: progress.interpolate({
        inputRange: [0, 0.4, 1, 1.5],
        outputRange: [0, 0.1, 1, 0],
      }),
      transform: [
        {
          translateX: progress.interpolate({
            inputRange: [0.5, 1, 2],
            outputRange: I18nManager.isRTL
              ? [-titleLeftOffset, 0, rightOffset]
              : [rightOffset, 0, -titleLeftOffset],
          }),
        },
      ],
    },
    backgroundStyle: {
      transform: [
        {
          translateX: progress.interpolate({
            inputRange: [0, 1, 2],
            outputRange: I18nManager.isRTL
              ? [-layouts.screen.width, 0, layouts.screen.width]
              : [layouts.screen.width, 0, -layouts.screen.width],
          }),
        },
      ],
    },
  };
}
Example #16
Source File: useTiming.ts    From react-native-paper-onboarding with MIT License 4 votes vote down vote up
useTiming = ({
  animatedStaticIndex,
  animatedOverrideIndex,
  value,
  velocity,
  state,
  size,
  screenWidth,
}: useTimingProps) => {
  const clock = useClock();
  const isManuallyAnimated = useValue(0);

  const config = useMemo(
    () => ({
      toValue: new Animated.Value(0),
      duration: 500,
      easing: Easing.out(Easing.exp),
    }),
    []
  );

  const animationState = useMemo(
    () => ({
      finished: new Animated.Value(0),
      position: new Animated.Value(0),
      frameTime: new Animated.Value(0),
      time: new Animated.Value(0),
    }),
    []
  );

  const valueClamp = useMemo(
    () =>
      interpolate(value, {
        inputRange: [screenWidth * -1, 0, screenWidth],
        outputRange: I18nManager.isRTL ? [-1, 0, 1] : [1, 0, -1],
        extrapolate: Animated.Extrapolate.CLAMP,
      }),
    [value, screenWidth]
  );

  const velocityClamp = useMemo(
    () =>
      interpolate(velocity, {
        inputRange: [screenWidth * -2, 0, screenWidth * 2],
        outputRange: I18nManager.isRTL ? [-0.5, 0, 0.5] : [0.5, 0, -0.5],
        extrapolate: Animated.Extrapolate.CLAMP,
      }),
    [screenWidth, velocity]
  );

  const isTimingInterrupted = and(eq(state, State.BEGAN), clockRunning(clock));
  const finishTiming = useMemo(
    () => [
      set(animatedStaticIndex, config.toValue),
      set(animatedOverrideIndex, config.toValue),
      set(animationState.frameTime, 0),
      set(animationState.time, 0),
      set(state, State.UNDETERMINED),
      set(isManuallyAnimated, 0),
      stopClock(clock),
    ],
    [
      state,
      animatedOverrideIndex,
      animatedStaticIndex,
      animationState.frameTime,
      animationState.time,
      clock,
      config.toValue,
      isManuallyAnimated,
    ]
  );

  const shouldAnimate = useMemo(
    () =>
      and(
        not(and(eq(animatedStaticIndex, 0), lessThan(valueClamp, 0))),
        not(and(eq(animatedStaticIndex, size - 1), greaterThan(valueClamp, 0)))
      ),
    [animatedStaticIndex, size, valueClamp]
  );
  const shouldReset = useMemo(
    () => not(greaterThan(add(abs(valueClamp), abs(velocityClamp)), 0.5)),
    [valueClamp, velocityClamp]
  );
  const shouldAnimateNext = useMemo(
    () =>
      greaterThan(
        add(animationState.position, velocityClamp),
        animatedStaticIndex
      ),
    [animatedStaticIndex, animationState.position, velocityClamp]
  );

  const animatedPosition = useMemo(
    () =>
      block([
        cond(isTimingInterrupted, finishTiming),
        cond(
          eq(state, State.ACTIVE),
          cond(
            and(
              not(and(eq(animatedStaticIndex, 0), lessThan(valueClamp, 0))),
              not(
                and(
                  eq(animatedStaticIndex, size - 1),
                  greaterThan(valueClamp, 0)
                )
              )
            ),
            [
              set(animationState.finished, 0),
              set(
                animationState.position,
                add(animatedStaticIndex, valueClamp)
              ),
            ]
          )
        ),

        onChange(animatedOverrideIndex, [
          set(isManuallyAnimated, 1),
          set(animationState.finished, 0),
        ]),

        cond(or(eq(state, State.END), isManuallyAnimated), [
          cond(and(not(clockRunning(clock)), not(animationState.finished)), [
            cond(
              isManuallyAnimated,
              set(config.toValue, animatedOverrideIndex),
              cond(
                or(shouldReset, not(shouldAnimate)),
                set(config.toValue, animatedStaticIndex),
                cond(
                  shouldAnimateNext,
                  set(config.toValue, add(animatedStaticIndex, 1)),
                  set(config.toValue, sub(animatedStaticIndex, 1))
                )
              )
            ),
            set(animationState.finished, 0),
            set(animationState.frameTime, 0),
            set(animationState.time, 0),
            startClock(clock),
          ]),
          timing(clock, animationState, config),
          cond(animationState.finished, finishTiming),
        ]),

        animationState.position,
      ]),
    [
      size,
      state,
      animatedOverrideIndex,
      animatedStaticIndex,
      animationState,
      clock,
      config,
      finishTiming,
      isManuallyAnimated,
      isTimingInterrupted,
      shouldAnimate,
      shouldAnimateNext,
      shouldReset,
      valueClamp,
    ]
  );
  return animatedPosition;
}
Example #17
Source File: PaperOnboarding.tsx    From react-native-paper-onboarding with MIT License 4 votes vote down vote up
PaperOnboardingComponent = forwardRef<
  PaperOnboarding,
  PaperOnboardingProps
>(
  (
    {
      data,
      safeInsets: _safeInsets,
      direction = DEFAULT_DIRECTION,
      // indicator config
      indicatorSize = DEFAULT_INDICATOR_SIZE,
      indicatorBackgroundColor = DEFAULT_INDICATOR_BACKGROUND_COLOR,
      indicatorBorderColor = DEFAULT_INDICATOR_BORDER_COLOR,
      // override styles
      titleStyle,
      descriptionStyle,
      // close button config
      closeButton,
      closeButtonTextStyle,
      closeButtonText = DEFAULT_CLOSE_BUTTON_TEXT,
      onCloseButtonPress = DEFAULT_CLOSE_BUTTON_CALLBACK,
      onIndexChange,
    },
    ref
  ) => {
    // state
    const [dimensions, setDimensions] =
      useState<PaperOnboardingScreenDimensions>({
        width: Dimensions.get('window').width,
        height: Dimensions.get('window').height,
      });

    // refs
    const indexRef = useRef<number>(0);
    const pagesRef = useRef<Array<Animated.View | null>>(data.map(() => null));

    //#region variables
    const safeInsets = useMemo<Required<Insets>>(() => {
      return {
        top: _safeInsets?.top ?? DEFAULT_SAFE_INSET,
        bottom: _safeInsets?.bottom ?? DEFAULT_SAFE_INSET,
        left: _safeInsets?.left ?? DEFAULT_SAFE_INSET,
        right: _safeInsets?.right ?? DEFAULT_SAFE_INSET,
      };
    }, [_safeInsets]);
    const indicatorsContainerLeftPadding = useMemo(() => {
      const containerLeftPadding = dimensions.width / 2 - indicatorSize / 2;
      return I18nManager.isRTL
        ? -containerLeftPadding + indicatorSize * (data.length - 1)
        : containerLeftPadding;
    }, [dimensions.width, indicatorSize, data.length]);
    //#endregion

    //#region animated variables
    const { gestureHandler, state, translation, velocity } =
      usePanGestureHandler();
    const animatedStaticIndex = useValue<number>(0);
    const animatedOverrideIndex = useValue<number>(0);
    const animatedIndex = useTiming({
      animatedStaticIndex,
      animatedOverrideIndex,
      value: direction === 'horizontal' ? translation.x : translation.y,
      velocity: direction === 'horizontal' ? velocity.x : velocity.y,
      state: state,
      size: data.length,
      screenWidth: dimensions.width,
    });
    const indicatorsContainerPosition = useMemo(
      () => data.map((_, index) => index * indicatorSize * -1),
      [data, indicatorSize]
    );
    const animatedIndicatorsContainerPosition = useMemo(
      () =>
        add(
          interpolate(animatedIndex, {
            inputRange: data.map((_, index) => index),
            outputRange: I18nManager.isRTL
              ? indicatorsContainerPosition.reverse()
              : indicatorsContainerPosition,
            extrapolate: Animated.Extrapolate.CLAMP,
          }),
          indicatorsContainerLeftPadding
        ),
      [
        data,
        animatedIndex,
        indicatorsContainerLeftPadding,
        indicatorsContainerPosition,
      ]
    );
    //#endregion

    //#region callbacks
    const handlePageRef = useCallback((pageRef, index) => {
      pagesRef.current[index] = pageRef;
    }, []);

    const handleOnLayout = useCallback(
      ({
        nativeEvent: {
          layout: { width, height },
        },
      }: LayoutChangeEvent) => {
        setDimensions({
          width,
          height,
        });
      },
      []
    );
    //#endregion

    //#region public methods
    const handleNavigateToNextPage = useCallback(() => {
      const currentIndex = indexRef.current;
      if (currentIndex === data.length - 1) {
        return;
      }
      animatedOverrideIndex.setValue(currentIndex + 1);
    }, [data, animatedOverrideIndex]);
    const handleNavigateToPreviousPage = useCallback(() => {
      const currentIndex = indexRef.current;
      if (currentIndex === 0) {
        return;
      }
      animatedOverrideIndex.setValue(currentIndex - 1);
    }, [animatedOverrideIndex]);
    useImperativeHandle(
      ref,
      () => ({
        next: handleNavigateToNextPage,
        previous: handleNavigateToPreviousPage,
      }),
      [handleNavigateToNextPage, handleNavigateToPreviousPage]
    );
    //#endregion

    //#region effects
    useCode(
      () =>
        onChange(
          animatedStaticIndex,
          call([animatedStaticIndex], args => {
            indexRef.current = args[0];
            /**
             * @DEV
             * here we directly manipulate pages native props by setting `pointerEvents`
             * to `auto` for current page and `none` for others.
             */
            pagesRef.current.map((pageRef, _index) => {
              // @ts-ignore
              pageRef.setNativeProps({
                pointerEvents: _index === args[0] ? 'auto' : 'none',
              });
            });

            if (onIndexChange) {
              onIndexChange(args[0]);
            }
          })
        ),
      []
    );
    //#endregion

    // renders
    return (
      <PanGestureHandler {...gestureHandler}>
        <Animated.View onLayout={handleOnLayout} style={styles.container}>
          <Background
            animatedIndex={animatedIndex}
            data={data}
            safeInsets={safeInsets}
            screenDimensions={dimensions}
            indicatorSize={indicatorSize}
            animatedIndicatorsContainerPosition={
              animatedIndicatorsContainerPosition
            }
          />

          {data.map((item, index) => (
            <Page
              key={`page-${index}`}
              index={index}
              item={item}
              animatedIndex={animatedIndex}
              indicatorSize={indicatorSize}
              titleStyle={titleStyle}
              descriptionStyle={descriptionStyle}
              safeInsets={safeInsets}
              screenDimensions={dimensions}
              handleRef={handlePageRef}
            />
          ))}

          <IndicatorsContainer
            data={data}
            animatedIndex={animatedIndex}
            animatedIndicatorsContainerPosition={
              animatedIndicatorsContainerPosition
            }
            indicatorSize={indicatorSize}
            indicatorBackgroundColor={indicatorBackgroundColor}
            indicatorBorderColor={indicatorBorderColor}
            safeInsets={safeInsets}
          />

          <CloseButton
            data={data}
            animatedIndex={animatedIndex}
            safeInsets={safeInsets}
            closeButtonText={closeButtonText}
            closeButtonTextStyle={closeButtonTextStyle}
            closeButton={closeButton}
            onCloseButtonPress={onCloseButtonPress}
          />
        </Animated.View>
      </PanGestureHandler>
    );
  }
)
Example #18
Source File: Ripple.tsx    From mobile with Apache License 2.0 4 votes vote down vote up
Ripple = ({
  children,
  disabled,
  rippleColor = 'rgb(0, 0, 0)',
  rippleCentered = false,
  rippleOpacity = 0.3,
  rippleExpandDuration = 500,
  rippleFadeDuration = 200,
  rippleContainerBorderRadius = 0,
  rippleSize = 0,
  ...touchableWithoutFeedbackProps
}: RippleProps) => {
  const [width, setWidth] = useState(0);
  const [height, setHeight] = useState(0);
  const uuid = useRef(0);
  const [ripples, setRipples] = useState<AnimatedRipple[]>([]);
  const [fadings, setFadings] = useState<number[]>([]);

  const startFade = useCallback(
    (ripple: AnimatedRipple, duration: number) => {
      if (fadings.indexOf(ripple.id) >= 0) {
        return;
      }
      setFadings([...fadings, ripple.id]);
      Animated.timing(ripple.fadeAnimatedValue, {
        toValue: 1,
        easing: Easing.out(Easing.ease),
        duration,
        useNativeDriver: true,
      }).start(() => {
        setRipples(ripples.filter(item => item !== ripple));
      });
    },
    [fadings, ripples],
  );

  const startExpand = useCallback(
    (event: GestureResponderEvent) => {
      if (!width || !height) {
        return;
      }

      const timestamp = Date.now();
      if (ripples.length > 0 && timestamp < ripples[ripples.length - 1].timestamp + DEBOUNCE) {
        return;
      }

      const w2 = 0.5 * width;
      const h2 = 0.5 * height;

      const {locationX, locationY} = rippleCentered ? {locationX: w2, locationY: h2} : event.nativeEvent;

      const offsetX = Math.abs(w2 - locationX);
      const offsetY = Math.abs(h2 - locationY);

      const radius = rippleSize > 0 ? 0.5 * rippleSize : Math.sqrt((w2 + offsetX) ** 2 + (h2 + offsetY) ** 2);

      const id = uuid.current;
      uuid.current += 1;

      const ripple: AnimatedRipple = {
        id,
        locationX,
        locationY,
        radius,
        timestamp,
        expandAnimatedValue: new Animated.Value(0),
        fadeAnimatedValue: new Animated.Value(0),
      };

      Animated.timing(ripple.expandAnimatedValue, {
        toValue: 1,
        easing: Easing.out(Easing.ease),
        duration: rippleExpandDuration,
        useNativeDriver: true,
      }).start();

      setRipples(ripples.concat(ripple));
    },
    [height, rippleCentered, rippleExpandDuration, rippleSize, ripples, width],
  );

  const onLayout = useCallback(
    (event: LayoutChangeEvent) => {
      const {
        nativeEvent: {
          layout: {height, width},
        },
      } = event;
      setWidth(width);
      setHeight(height);
      touchableWithoutFeedbackProps.onLayout?.(event);
    },
    [touchableWithoutFeedbackProps.onLayout],
  );

  const onPressIn = useCallback(
    (event: GestureResponderEvent) => {
      startExpand(event);
      touchableWithoutFeedbackProps.onPressIn?.(event);
    },
    [startExpand, touchableWithoutFeedbackProps.onPressIn],
  );

  const onPressOut = useCallback(
    (event: GestureResponderEvent) => {
      ripples.forEach(ripple => startFade(ripple, rippleFadeDuration + rippleExpandDuration / 2));
      touchableWithoutFeedbackProps.onPressOut?.(event);
    },
    [rippleExpandDuration, rippleFadeDuration, ripples, startFade, touchableWithoutFeedbackProps.onPressOut],
  );

  const onPress = useCallback(
    (event: GestureResponderEvent) => {
      requestAnimationFrame(() => {
        touchableWithoutFeedbackProps.onPress?.(event);
      });
    },
    [touchableWithoutFeedbackProps.onPress],
  );

  const renderRipple = useCallback(
    ({locationX, locationY, radius, id, expandAnimatedValue, fadeAnimatedValue}: AnimatedRipple) => {
      const rippleStyle = {
        top: locationY - RADIUS,
        [I18nManager.isRTL ? 'right' : 'left']: locationX - RADIUS,
        backgroundColor: rippleColor,
        transform: [
          {
            scale: expandAnimatedValue.interpolate({
              inputRange: [0, 1],
              outputRange: [0.5 / RADIUS, radius / RADIUS],
            }),
          },
        ],
        opacity: fadeAnimatedValue.interpolate({
          inputRange: [0, 1],
          outputRange: [rippleOpacity, 0],
        }),
      };
      return <Animated.View style={[styles.ripple, rippleStyle]} key={id} />;
    },
    [rippleColor, rippleOpacity],
  );

  const style = useMemo(
    () => [
      styles.container,
      {
        borderRadius: rippleContainerBorderRadius,
      },
    ],
    [rippleContainerBorderRadius],
  );

  return (
    <TouchableWithoutFeedback
      {...touchableWithoutFeedbackProps}
      disabled={disabled}
      onPressIn={onPressIn}
      onPressOut={onPressOut}
      onPress={onPress}
      onLayout={onLayout}
    >
      <Animated.View pointerEvents="box-only">
        <View style={style}>{ripples.map(renderRipple)}</View>
        {children}
      </Animated.View>
    </TouchableWithoutFeedback>
  );
}
Example #19
Source File: AnimatedTabBar.tsx    From curved-bottom-navigation-bar with MIT License 4 votes vote down vote up
AnimatedTabBarComponent = (props: AnimatedTabBarProps) => {
  // props
  const {
    navigation,
    tabs,
    descriptors,
    state,
    duration = DEFAULT_ITEM_ANIMATION_DURATION,
    barColor = TAB_BAR_COLOR,
    dotSize = SIZE_DOT,
    barHeight = TAB_BAR_HEIGHT,
    dotColor = TAB_BAR_COLOR,
    titleShown = false,
    barWidth,
  } = props;

  // variables

  const {
    routes,
    index: navigationIndex,
    key: navigationKey,
  } = useMemo(() => {
    return state;
  }, [state]);

  // reanimated
  const selectedIndex = useSharedValue(0);

  // callbacks
  const getRouteTitle = useCallback(
    (route: Route<string>) => {
      const { options } = descriptors[route.key];
      // eslint-disable-next-line no-nested-ternary
      return options.tabBarLabel !== undefined &&
        typeof options.tabBarLabel === 'string'
        ? options.tabBarLabel
        : options.title !== undefined
        ? options.title
        : route.name;
    },
    [descriptors]
  );

  const getRouteTabConfigs = useCallback(
    (route: Route<string>) => {
      return tabs[route.name];
    },
    [tabs]
  );

  const getRoutes = useCallback(() => {
    return routes.map((route) => ({
      key: route.key,
      title: getRouteTitle(route),
      ...getRouteTabConfigs(route),
    }));
  }, [routes, getRouteTitle, getRouteTabConfigs]);

  const handleSelectedIndexChange = useCallback(
    (index: number) => {
      const { key, name } = routes[index];
      const event = navigation.emit({
        type: 'tabPress',
        target: key,
        canPreventDefault: true,
      });

      if (!event.defaultPrevented) {
        navigation.dispatch({
          ...CommonActions.navigate(name),
          target: navigationKey,
        });
      }
    },
    [routes, navigation, navigationKey]
  );

  // effects
  useEffect(() => {
    selectedIndex.value = navigationIndex;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [navigationIndex]);

  useAnimatedReaction(
    () => selectedIndex.value,
    (nextSelected, prevSelected) => {
      if (nextSelected !== prevSelected) {
        runOnJS(handleSelectedIndexChange)(nextSelected);
      }
    },
    [selectedIndex, handleSelectedIndexChange]
  );

  // render
  return (
    <CurvedTabBar
      isRtl={I18nManager.isRTL}
      barWidth={barWidth}
      titleShown={titleShown}
      dotColor={dotColor}
      barHeight={barHeight}
      dotSize={dotSize}
      tabBarColor={barColor}
      selectedIndex={selectedIndex}
      navigationIndex={navigationIndex}
      routes={getRoutes()}
      duration={duration}
    />
  );
}