UNPKG

vcc-ui

Version:

VCC UI is a collection of React UI Components that can be used for developing front-end applications at Volvo Car Corporation.

275 lines (251 loc) 7.16 kB
import React, { createContext, useState, useRef } from 'react'; import { useFela } from 'react-fela'; import PropTypes from 'prop-types'; import { Block } from '../block'; import { Click } from '../click'; import { Arrow } from '../arrow'; import { Inline } from '../inline'; import { Flex } from '../flex'; import { getThemeStyle } from '../../get-theme-style'; import { deprecate } from '../../deprecate'; export const TabNavContext = createContext({}); const styles = { nav: ({ theme, reverseOut }) => ({ flexDirection: 'row', overflow: 'hidden', borderBottomStyle: 'solid', borderBottomWidth: 1, borderBottomColor: reverseOut ? theme.color.primitive.grey200 : theme.color.ornament.divider, backgroundColor: reverseOut ? theme.color.primitive.grey200 : theme.color.background.secondary, }), scrollContainer: { flex: '0 1 auto', overflowY: 'hidden', overflowX: 'auto', msOverflowY: 'hidden', msOverflowX: 'auto', msOverflowStyle: 'none', '::-webkit-scrollbar': { display: 'none', }, }, itemContainer: { flexDirection: 'row', padding: '0 0 0 19px', }, stickyLeft: ({ isCentered }) => ({ flexShrink: 0, flexBasis: 0, flexGrow: isCentered ? 1 : 0, msFlex: '1 0 auto', flexDirection: 'row', justifyContent: 'flex-start', alignItems: 'center', }), stickyRight: { flexShrink: 0, flexBasis: 0, flexGrow: 1, msFlex: '1 0 auto', alignItems: 'center', justifyContent: 'flex-end', flexDirection: 'row', }, border: ({ reverseOut, animatedMargin, duration, theme }) => ({ width: 0, height: duration === 0 ? 0 : 3, marginRight: '0 !important', backgroundColor: reverseOut ? theme.color.primitive.white : theme.color.ornament.highlight, transitionProperty: `margin-${animatedMargin}, width`, transitionTimingFunction: 'ease-out', transitionDuration: duration + 'ms', }), backButton: ({ reverseOut, showBackButtonOn = [], theme }) => ({ fontSize: 15, padding: '16px 10px 19px', verticalAlign: 'middle', fontWeight: 200, whiteSpace: 'nowrap', flexShrink: '0', color: reverseOut ? theme.color.primitive.white : theme.color.foreground.primary, display: showBackButtonOn.some(size => size === 's') ? 'inline' : 'none', '> svg': { marginRight: 8, }, ':hover > svg': { transform: 'translateX(-5px)', }, [theme.breakpoints.onlyM]: { display: showBackButtonOn.some(size => size === 'm') ? 'inline' : 'none', }, [theme.breakpoints.fromL]: { display: showBackButtonOn.some(size => size === 'l') ? 'inline' : 'none', }, }), backButtonText: ({ theme }) => ({ [theme.breakpoints.onlyS]: { display: 'none', }, }), }; function BackButton({ backButtonConfig = {}, showBackButtonOn, reverseOut }) { const { theme } = useFela(); const { href, clickHandler = () => {}, text } = backButtonConfig; const styleProps = { theme, reverseOut, showBackButtonOn, }; return ( <Click onClick={clickHandler} href={href} extend={[ styles.backButton(styleProps), getThemeStyle('tabNavBackButton', theme, styleProps), ]} > <Arrow direction="left" size={10} color={ reverseOut ? theme.color.primitive.white : theme.color.ornament.highlight } /> <Inline extend={styles.backButtonText}>{text || ''}</Inline> </Click> ); } function TabNavBorder() { const { borderRef, reverseOut, duration } = React.useContext(TabNavContext); const { theme } = useFela(); const animatedMargin = theme.direction === 'rtl' ? 'right' : 'left'; return ( <Block ref={borderRef} extend={styles.border({ animatedMargin, duration, reverseOut, theme, })} /> ); } export const TabNav = React.forwardRef( ( { children, duration = 160, enableLineTransition = false, textAlign = 'center', // TODO: remove variant in 2.x // eslint-disable-next-line variant, stickyChildrenLeft = null, stickyChildrenRight = null, showBackButtonOn = ['s', 'm', 'l'], backButton: backButtonConfig = {}, }, ref ) => { const [active, setActive] = useState(null); const scrollRef = useRef(); const borderRef = useRef(); const { theme } = useFela(); // TODO: remove in 2.x deprecate( 'Variant for TabNav is deprecated, please use the volvo dark theme instead', variant ); const reverseOut = variant === 'dark'; const isCentered = textAlign === 'center'; const backButtonVisible = !!( backButtonConfig.clickHandler || backButtonConfig.href ); const styleProps = { theme, reverseOut, isCentered, }; const context = { active, setActive, scrollRef, borderRef, reverseOut, duration: enableLineTransition ? duration : 0, }; return ( <Flex as="nav" ref={ref} extend={[ styles.nav(styleProps), getThemeStyle('tabNav', theme, styleProps), ]} > <Flex extend={styles.stickyLeft(styleProps)}> {backButtonVisible ? ( <BackButton backButtonConfig={backButtonConfig} showBackButtonOn={showBackButtonOn} reverseOut={reverseOut} /> ) : null} <TabNavContext.Provider value={{ reverseOut }}> {stickyChildrenLeft} </TabNavContext.Provider> </Flex> <TabNavContext.Provider value={context}> <Flex ref={scrollRef} extend={styles.scrollContainer}> <Flex extend={styles.itemContainer}> <Flex extend={{ paddingRight: 1 }} /> {children} <Flex extend={{ paddingRight: 1 }} /> </Flex> <TabNavBorder /> </Flex> </TabNavContext.Provider> <Flex extend={styles.stickyRight}> <TabNavContext.Provider value={{ reverseOut }}> {stickyChildrenRight} </TabNavContext.Provider> </Flex> </Flex> ); } ); TabNav.displayName = 'TabNav'; TabNav.propTypes = { /** Text-align: left or center */ textAlign: PropTypes.oneOf(['center', 'left']), /** Back button text, href, click handler */ backButton: PropTypes.shape({ text: PropTypes.string, href: PropTypes.string, clickHandler: PropTypes.func, }), /** A JSX node */ children: PropTypes.node, /** A JSX node sticky to the left */ stickyChildrenLeft: PropTypes.node, /** A JSX node sticky to the right */ stickyChildrenRight: PropTypes.node, /** Which viewports to show the back button on */ showBackButtonOn: PropTypes.arrayOf(PropTypes.oneOf(['s', 'm', 'l'])), /** Enable fancy transition when changing active tabs */ enableLineTransition: PropTypes.bool, /** Set duration of lineTransition animation */ duration: PropTypes.number, };