"use strict";

import { getHeaderTitle, Header, SafeAreaProviderCompat, Screen } from '@react-navigation/elements';
import { StackActions } from '@react-navigation/native';
import * as React from 'react';
import { Animated, Platform, StyleSheet } from 'react-native';
import { SafeAreaInsetsContext } from 'react-native-safe-area-context';
import { FadeTransition, ShiftTransition } from "../TransitionConfigs/TransitionPresets.js";
import { BottomTabBarHeightCallbackContext } from "../utils/BottomTabBarHeightCallbackContext.js";
import { BottomTabBarHeightContext } from "../utils/BottomTabBarHeightContext.js";
import { useAnimatedHashMap } from "../utils/useAnimatedHashMap.js";
import { BottomTabBar, getTabBarHeight } from "./BottomTabBar.js";
import { MaybeScreen, MaybeScreenContainer } from "./ScreenFallback.js";
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
const EPSILON = 1e-5;
const STATE_INACTIVE = 0;
const STATE_TRANSITIONING_OR_BELOW_TOP = 1;
const STATE_ON_TOP = 2;
const NAMED_TRANSITIONS_PRESETS = {
  fade: FadeTransition,
  shift: ShiftTransition,
  none: {
    sceneStyleInterpolator: undefined,
    transitionSpec: {
      animation: 'timing',
      config: {
        duration: 0
      }
    }
  }
};
const useNativeDriver = Platform.OS !== 'web';
const hasAnimation = options => {
  const {
    animation,
    transitionSpec
  } = options;
  if (animation) {
    return animation !== 'none';
  }
  return Boolean(transitionSpec);
};
const renderTabBarDefault = props => /*#__PURE__*/_jsx(BottomTabBar, {
  ...props
});
export function BottomTabView(props) {
  const {
    tabBar = renderTabBarDefault,
    state,
    navigation,
    descriptors,
    safeAreaInsets,
    detachInactiveScreens = Platform.OS === 'web' || Platform.OS === 'android' || Platform.OS === 'ios'
  } = props;
  const focusedRouteKey = state.routes[state.index].key;

  /**
   * List of loaded tabs, tabs will be loaded when navigated to.
   */
  const [loaded, setLoaded] = React.useState([focusedRouteKey]);
  if (!loaded.includes(focusedRouteKey)) {
    // Set the current tab to be loaded if it was not loaded before
    setLoaded([...loaded, focusedRouteKey]);
  }
  const previousRouteKeyRef = React.useRef(focusedRouteKey);
  const tabAnims = useAnimatedHashMap(state);
  React.useEffect(() => {
    const previousRouteKey = previousRouteKeyRef.current;
    let popToTopAction;
    if (previousRouteKey !== focusedRouteKey && descriptors[previousRouteKey]?.options.popToTopOnBlur) {
      const prevRoute = state.routes.find(route => route.key === previousRouteKey);
      if (prevRoute?.state?.type === 'stack' && prevRoute.state.key) {
        popToTopAction = {
          ...StackActions.popToTop(),
          target: prevRoute.state.key
        };
      }
    }
    const animateToIndex = () => {
      if (previousRouteKey !== focusedRouteKey) {
        navigation.emit({
          type: 'transitionStart',
          target: focusedRouteKey
        });
      }
      Animated.parallel(state.routes.map((route, index) => {
        const {
          options
        } = descriptors[route.key];
        const {
          animation = 'none',
          transitionSpec = NAMED_TRANSITIONS_PRESETS[animation].transitionSpec
        } = options;
        let spec = transitionSpec;
        if (route.key !== previousRouteKey && route.key !== focusedRouteKey) {
          // Don't animate if the screen is not previous one or new one
          // This will avoid flicker for screens not involved in the transition
          spec = NAMED_TRANSITIONS_PRESETS.none.transitionSpec;
        }
        spec = spec ?? NAMED_TRANSITIONS_PRESETS.none.transitionSpec;
        const toValue = index === state.index ? 0 : index >= state.index ? 1 : -1;
        return Animated[spec.animation](tabAnims[route.key], {
          ...spec.config,
          toValue,
          useNativeDriver
        });
      }).filter(Boolean)).start(({
        finished
      }) => {
        if (finished && popToTopAction) {
          navigation.dispatch(popToTopAction);
        }
        if (previousRouteKey !== focusedRouteKey) {
          navigation.emit({
            type: 'transitionEnd',
            target: focusedRouteKey
          });
        }
      });
    };
    animateToIndex();
    previousRouteKeyRef.current = focusedRouteKey;
  }, [descriptors, focusedRouteKey, navigation, state.index, state.routes, tabAnims]);
  const dimensions = SafeAreaProviderCompat.initialMetrics.frame;
  const [tabBarHeight, setTabBarHeight] = React.useState(() => getTabBarHeight({
    state,
    descriptors,
    dimensions,
    insets: {
      ...SafeAreaProviderCompat.initialMetrics.insets,
      ...props.safeAreaInsets
    },
    style: descriptors[state.routes[state.index].key].options.tabBarStyle
  }));
  const renderTabBar = () => {
    return /*#__PURE__*/_jsx(SafeAreaInsetsContext.Consumer, {
      children: insets => tabBar({
        state: state,
        descriptors: descriptors,
        navigation: navigation,
        insets: {
          top: safeAreaInsets?.top ?? insets?.top ?? 0,
          right: safeAreaInsets?.right ?? insets?.right ?? 0,
          bottom: safeAreaInsets?.bottom ?? insets?.bottom ?? 0,
          left: safeAreaInsets?.left ?? insets?.left ?? 0
        }
      })
    });
  };
  const {
    routes
  } = state;

  // If there is no animation, we only have 2 states: visible and invisible
  const hasTwoStates = !routes.some(route => hasAnimation(descriptors[route.key].options));
  const {
    tabBarPosition = 'bottom'
  } = descriptors[focusedRouteKey].options;
  const tabBarElement = /*#__PURE__*/_jsx(BottomTabBarHeightCallbackContext.Provider, {
    value: setTabBarHeight,
    children: renderTabBar()
  }, "tabbar");
  return /*#__PURE__*/_jsxs(SafeAreaProviderCompat, {
    style: {
      flexDirection: tabBarPosition === 'left' || tabBarPosition === 'right' ? 'row' : 'column'
    },
    children: [tabBarPosition === 'top' || tabBarPosition === 'left' ? tabBarElement : null, /*#__PURE__*/_jsx(MaybeScreenContainer, {
      enabled: detachInactiveScreens,
      hasTwoStates: hasTwoStates,
      style: styles.screens,
      children: routes.map((route, index) => {
        const descriptor = descriptors[route.key];
        const {
          lazy = true,
          animation = 'none',
          sceneStyleInterpolator = NAMED_TRANSITIONS_PRESETS[animation].sceneStyleInterpolator
        } = descriptor.options;
        const isFocused = state.index === index;
        const isPreloaded = state.preloadedRouteKeys.includes(route.key);
        if (lazy && !loaded.includes(route.key) && !isFocused && !isPreloaded) {
          // Don't render a lazy screen if we've never navigated to it or it wasn't preloaded
          return null;
        }
        const {
          freezeOnBlur,
          header = ({
            layout,
            options
          }) => /*#__PURE__*/_jsx(Header, {
            ...options,
            layout: layout,
            title: getHeaderTitle(options, route.name)
          }),
          headerShown,
          headerStatusBarHeight,
          headerTransparent,
          sceneStyle: customSceneStyle
        } = descriptor.options;
        const {
          sceneStyle
        } = sceneStyleInterpolator?.({
          current: {
            progress: tabAnims[route.key]
          }
        }) ?? {};
        const animationEnabled = hasAnimation(descriptor.options);
        const activityState = isFocused ? STATE_ON_TOP // the screen is on top after the transition
        : animationEnabled // is animation is not enabled, immediately move to inactive state
        ? tabAnims[route.key].interpolate({
          inputRange: [0, 1 - EPSILON, 1],
          outputRange: [STATE_TRANSITIONING_OR_BELOW_TOP,
          // screen visible during transition
          STATE_TRANSITIONING_OR_BELOW_TOP, STATE_INACTIVE // the screen is detached after transition
          ],
          extrapolate: 'extend'
        }) : STATE_INACTIVE;
        return /*#__PURE__*/_jsx(MaybeScreen, {
          style: [StyleSheet.absoluteFill, {
            zIndex: isFocused ? 0 : -1
          }],
          active: activityState,
          enabled: detachInactiveScreens,
          freezeOnBlur: freezeOnBlur,
          shouldFreeze: activityState === STATE_INACTIVE && !isPreloaded,
          children: /*#__PURE__*/_jsx(BottomTabBarHeightContext.Provider, {
            value: tabBarPosition === 'bottom' ? tabBarHeight : 0,
            children: /*#__PURE__*/_jsx(Screen, {
              focused: isFocused,
              route: descriptor.route,
              navigation: descriptor.navigation,
              headerShown: headerShown,
              headerStatusBarHeight: headerStatusBarHeight,
              headerTransparent: headerTransparent,
              header: header({
                layout: dimensions,
                route: descriptor.route,
                navigation: descriptor.navigation,
                options: descriptor.options
              }),
              style: [customSceneStyle, animationEnabled && sceneStyle],
              children: descriptor.render()
            })
          })
        }, route.key);
      })
    }, "screens"), tabBarPosition === 'bottom' || tabBarPosition === 'right' ? tabBarElement : null]
  });
}
const styles = StyleSheet.create({
  screens: {
    flex: 1,
    overflow: 'hidden'
  }
});
//# sourceMappingURL=BottomTabView.js.map