<commit_before>import { Box, color, Flex, Sans, space, Spacer } from "@artsy/palette"
import { useScreenDimensions } from "lib/utils/useScreenDimensions"
import React, { useState } from "react"
import { TouchableOpacity, View } from "react-native"
import Animated from "react-native-reanimated"
import styled from "styled-components/native"
import { useAnimatedValue } from "./reanimatedHelpers"
import { SnappyHorizontalRail } from "./SnappyHorizontalRail"
import { StickyTabScrollView } from "./StickyTabScrollView"

interface TabProps {
  initial?: boolean
  title: string
  content: JSX.Element
}

/**
 * This page wrapper encapsulates a disappearing header and sticky tabs each with their own content
 *
 * At the moment all tabs are rendered at all times, as this isn't designed for more than 3 tabs
 * but if we need to have conditional rendering of tab content in the future it should be possible.
 *
 * Each tab optionally consumes a 'scroll view context' which could potentialy contain information
 * about whether the tab is being shown currently etc.
 */
export const StickyTabPage: React.FC<{
  tabs: TabProps[]
  headerContent: JSX.Element
}> = ({ tabs, headerContent }) => {
  const { width } = useScreenDimensions()
  const [activeTabIndex, setActiveTabIndex] = useState(Math.max(tabs.findIndex(tab => tab.initial), 0))
  const [headerHeight, setHeaderHeight] = useState<null | number>(null)

  const headerOffsetY = useAnimatedValue(0)

  return (
    <View style={{ flex: 1, position: "relative", overflow: "hidden" }}>
      {/* put tab content first because we want the header to be absolutely positioned _above_ it */}
      {headerHeight !== null && (
        <SnappyHorizontalRail offset={activeTabIndex * width}>
          {tabs.map(({ content }, index) => {
            return (
              <View style={{ flex: 1, width }} key={index}>
                <StickyTabScrollView
                  headerHeight={headerHeight}
                  content={content}
                  headerOffsetY={headerOffsetY}
                  isActive={index === activeTabIndex}
                />
              </View>
            )
          })}
        </SnappyHorizontalRail>
      )}
      <Animated.View
        style={{
          width,
          top: 0,
          position: "absolute",
          backgroundColor: "white",
          transform: [{ translateY: headerOffsetY as any }],
        }}
      >
        <View onLayout={e => setHeaderHeight(e.nativeEvent.layout.height)}>
          {headerContent}
          <Spacer mb={1} />
        </View>
        <StickyTabBar>
          {tabs.map(({ title }, index) => (
            <StickyTab
              key={title}
              label={title}
              active={activeTabIndex === index}
              onPress={() => {
                setActiveTabIndex(index)
              }}
            /><commit_msg>use palette color<commit_after>import { Box, color, Flex, Sans, space, Spacer } from "@artsy/palette"
import { useScreenDimensions } from "lib/utils/useScreenDimensions"
import React, { useState } from "react"
import { TouchableOpacity, View } from "react-native"
import Animated from "react-native-reanimated"
import styled from "styled-components/native"
import { useAnimatedValue } from "./reanimatedHelpers"
import { SnappyHorizontalRail } from "./SnappyHorizontalRail"
import { StickyTabScrollView } from "./StickyTabScrollView"

interface TabProps {
  initial?: boolean
  title: string
  content: JSX.Element
}

/**
 * This page wrapper encapsulates a disappearing header and sticky tabs each with their own content
 *
 * At the moment all tabs are rendered at all times, as this isn't designed for more than 3 tabs
 * but if we need to have conditional rendering of tab content in the future it should be possible.
 *
 * Each tab optionally consumes a 'scroll view context' which could potentialy contain information
 * about whether the tab is being shown currently etc.
 */
export const StickyTabPage: React.FC<{
  tabs: TabProps[]
  headerContent: JSX.Element
}> = ({ tabs, headerContent }) => {
  const { width } = useScreenDimensions()
  const [activeTabIndex, setActiveTabIndex] = useState(Math.max(tabs.findIndex(tab => tab.initial), 0))
  const [headerHeight, setHeaderHeight] = useState<null | number>(null)

  const headerOffsetY = useAnimatedValue(0)

  return (
    <View style={{ flex: 1, position: "relative", overflow: "hidden" }}>
      {/* put tab content first because we want the header to be absolutely positioned _above_ it */}
      {headerHeight !== null && (
        <SnappyHorizontalRail offset={activeTabIndex * width}>
          {tabs.map(({ content }, index) => {
            return (
              <View style={{ flex: 1, width }} key={index}>
                <StickyTabScrollView
                  headerHeight={headerHeight}
                  content={content}
                  headerOffsetY={headerOffsetY}
                  isActive={index === activeTabIndex}
                />
              </View>
            )
          })}
        </SnappyHorizontalRail>
      )}
      <Animated.View
        style={{
          width,
          top: 0,
          position: "absolute",
          backgroundColor: color("white100"),
          transform: [{ translateY: headerOffsetY as any }],
        }}
      >
        <View onLayout={e => setHeaderHeight(e.nativeEvent.layout.height)}>
          {headerContent}
          <Spacer mb={1} />
        </View>
        <StickyTabBar>
          {tabs.map(({ title }, index) => (
            <StickyTab
              key={title}
              label={title}
              active={activeTabIndex === index}
              onPress={() => {
                setActiveTabIndex(index)
              }}
            />
{'fraction_non_alphanumeric': 0.10917941585535466, 'fraction_numerical': 0.0029554937413073714, 'mean_word_length': 2.471937235968618, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6608063', 'n_tokens_mistral': 1554, 'n_tokens_neox': 1510, 'n_words': 464}