import { gloss } from 'gloss' import React, { cloneElement, isValidElement, memo, useCallback, useRef, useState } from 'react' import { useGet } from './hooks/useGet' import { useParentNodeSize } from './hooks/useParentNodeSize' import { SliderPane } from './SliderPane' import { View } from './View/View' import { useVisibility } from './Visibility' export type SliderProps = { /** Index of Slider pane to show */ curFrame: number /** Place inside here */ children?: React.ReactNode /** Padding to add inside frame */ framePad?: number /** Vertical padding inside frame */ verticalPad?: number /** Measure and ensure height of slider stays equal to tallest child */ fixHeightToTallest?: boolean /** Measure and ensure height of slider stays equal to parent node */ fixHeightToParent?: boolean /** Define an animation for transitioning */ transition?: string /** Define a height */ height?: string /** Define a width */ width?: string } export const Slider = memo((props: SliderProps) => { const { curFrame = 0, children, framePad = 0, verticalPad = 0, transition = 'transform ease 200ms, opacity ease 200ms', fixHeightToTallest, fixHeightToParent, ...rest } = props const frameRef = useRef(null) const [heights, setHeights] = useState([]) let currentHeight = 0 const visible = useVisibility() const getProps = useGet(props) const [numMounted, setNumMounted] = useState(React.Children.count(children)) const handleDidMount = useCallback(() => { setNumMounted(React.Children.count(getProps().children)) }, []) const size = useParentNodeSize({ ref: frameRef, disable: !visible, throttle: 120, }) if (fixHeightToParent) { currentHeight = size.height } else { if (fixHeightToTallest) { currentHeight = heights.reduce((a, b) => a + b, 0) } else { currentHeight = heights[curFrame] } } const width = rest.width || size.width const height = rest.height || currentHeight return ( {React.Children.map(children, (child, index) => { if (!isValidElement(child)) { throw new Error(`Must pass to `) } const isMounting = index > numMounted - 1 const isActive = !isMounting && curFrame === index const onChangeHeight = (next: number) => { heights[index] = next setHeights(heights) } return cloneElement(child as any, { framePad, verticalPad, fixHeightToTallest, currentHeight: height, curFrame, isActive, transition, width, index, onChangeHeight, onMountChange: handleDidMount, }) })} ) }) // @ts-ignore Slider.Pane = SliderPane const SliderContainer = gloss(View, { flex: 1, flexDirection: 'row', alignItems: 'flex-start', overflow: 'hidden', position: 'relative', })