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.

216 lines (187 loc) 8.36 kB
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } import React, { useRef, useContext, useEffect } from 'react'; import { useFela } from 'react-fela'; import PropTypes from 'prop-types'; import { Click } from '../click'; import { TabNavContext } from '../tab-nav'; import { getThemeStyle } from '../../get-theme-style'; var style = function style(_ref) { var reverseOut = _ref.reverseOut, isActiveTab = _ref.isActiveTab, duration = _ref.duration, theme = _ref.theme; return { minWidth: 50, alignSelf: 'initial', justifyContent: 'center', flexGrow: 0, flexShrink: 0, flexBasis: 'auto', whiteSpace: 'nowrap', paddingTop: theme.baselineGrid * 2, paddingBottom: theme.baselineGrid * 2, display: 'flex', marginRight: theme.baselineGrid * 2.5, transitionProperty: 'color', transitionTimingFunction: 'ease-out', transitionDuration: duration + 'ms', color: reverseOut ? theme.color.primitive.white : theme.color.foreground.primary, textAlign: 'left', letterSpacing: 0.3, fontWeight: 200, fontSize: 16, boxSizing: 'border-box', borderBottomWidth: duration === 0 ? 3 : 0, borderBottomStyle: 'solid', borderBottomColor: 'transparent', ':focus': { outline: 0, color: reverseOut ? theme.color.primitive.white : theme.color.ornament.highlight }, ':hover': { color: reverseOut ? theme.color.primitive.white : theme.color.ornament.highlight }, extend: [{ condition: isActiveTab, style: { borderBottomColor: reverseOut ? theme.color.primitive.white : theme.color.ornament.highlight, color: reverseOut ? theme.color.primitive.white : theme.color.ornament.highlight } }, { condition: duration === 0, style: { ':hover': { borderBottomColor: reverseOut ? theme.color.primitive.white : theme.color.ornament.highlight } } }] }; }; function calculatePosition(currentItem, newItem, scrollItem, isRTL) { var currentOffset = currentItem ? currentItem.offsetLeft : 0; var currentWidth = currentItem ? currentItem.offsetWidth : 0; var newWidth = newItem.offsetWidth; var newOffset = newItem.offsetLeft; var currentDifference = !isRTL ? currentOffset - scrollItem.offsetLeft : scrollItem.offsetWidth - (currentOffset - scrollItem.offsetLeft) - newWidth; var newDifference = !isRTL ? newOffset - scrollItem.offsetLeft : scrollItem.offsetWidth - (newOffset - scrollItem.offsetLeft) - newWidth; return { currentOffset: currentOffset, currentWidth: currentWidth, currentDifference: currentDifference, newWidth: newWidth, newOffset: newOffset, newDifference: newDifference }; } function activateTab(setActive, currentItem, newItem, scrollItem, borderItem, duration, scrollTolerance, isRTL) { var _calculatePosition = calculatePosition(currentItem, newItem, scrollItem, isRTL), currentOffset = _calculatePosition.currentOffset, currentWidth = _calculatePosition.currentWidth, currentDifference = _calculatePosition.currentDifference, newOffset = _calculatePosition.newOffset, newWidth = _calculatePosition.newWidth, newDifference = _calculatePosition.newDifference; var property = isRTL ? 'marginRight' : 'marginLeft'; var toLeft = currentOffset > newOffset; // just a small helper to conditionally apply styles to the border element function setStyle(style, value) { var condition = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; if (condition) { borderItem.style[style] = value + 'px'; } } // scroll into view if scrollItem is actually collapsed if (scrollItem.offsetWidth < scrollItem.scrollWidth && scrollItem.offsetWidth > newWidth) { var newScrollLeft = newItem.offsetLeft - scrollItem.offsetLeft - scrollItem.scrollLeft; var spaceRight = scrollItem.offsetWidth - newWidth - newScrollLeft; if (spaceRight - scrollTolerance < 0 || scrollItem.offsetWidth - (spaceRight + scrollTolerance) < newWidth) { scrollItem.scrollLeft = newScrollLeft; } } // set the new active item first to make sure calculation is always correct setActive(newItem); // initial load if (!currentItem) { setStyle('width', newWidth); setStyle(property, newDifference); return; } /** * Depending on the direction, it transforms the width and the x-offset * It uses a helper div (borderRef, see tab-nav/TabNavItemBorder) that is usually hidden * It removes the current TabNavItem border and adds it back once the animation is done */ if (toLeft) { setStyle('width', currentOffset - newOffset + currentWidth); setStyle(property, newDifference, !isRTL); setTimeout(function () { setStyle(property, newDifference, isRTL); setStyle('width', newWidth); }, duration * 3 / 2); } else { setStyle(property, newDifference, isRTL); setStyle('width', newWidth + currentDifference - newDifference, isRTL); setStyle('width', newWidth + newDifference - currentDifference, !isRTL); setTimeout(function () { setStyle('width', newWidth); setStyle(property, newDifference, !isRTL); }, duration * 3 / 2); } } export var TabNavItem = React.forwardRef(function (_ref2, _ref3) { var children = _ref2.children, isActive = _ref2.isActive, onClick = _ref2.onClick, props = _objectWithoutProperties(_ref2, ["children", "isActive", "onClick"]); var _useContext = useContext(TabNavContext), active = _useContext.active, setActive = _useContext.setActive, scrollRef = _useContext.scrollRef, borderRef = _useContext.borderRef, reverseOut = _useContext.reverseOut, _useContext$duration = _useContext.duration, duration = _useContext$duration === void 0 ? 0 : _useContext$duration; var _useFela = useFela(), theme = _useFela.theme; var itemRef = useRef(); var isRTL = theme.direction !== 'ltr'; var activate = function activate() { return activateTab(setActive, active, itemRef.current, scrollRef.current, borderRef.current, duration, theme.baselineGrid * 2.5, isRTL); }; useEffect(function () { if (isActive && setActive) { activate(); } }, // also update the active state if isActive is manually updated [isActive, isRTL]); var styleProps = { isActiveTab: active ? active === itemRef.current : isActive, reverseOut: reverseOut, duration: duration, theme: theme }; var onClickHandler = function onClickHandler(e) { if (setActive) { activate(); } if (onClick) { onClick(e); } }; return React.createElement(Click, _extends({}, props, { ref: function ref(el) { if (itemRef) itemRef.current = el; if (_ref3) _ref3.current = el; }, onClick: onClickHandler, extend: [style(styleProps), getThemeStyle('tabNavItem', theme, styleProps)] }), children); }); TabNavItem.displayName = 'TabNavItem'; TabNavItem.propTypes = { children: PropTypes.node, /** Indicate if the TabNavItem will open a dropdown menu */ isDropdown: PropTypes.bool, /** Indicate if the TabNavItem is in an active state */ isActive: PropTypes.bool, /** Onclick function */ onClick: PropTypes.func };