UNPKG

vcc-ui

Version:

A React library for building user interfaces at Volvo Cars

218 lines (212 loc) 7.85 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.TabNavItem = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _propTypes = _interopRequireDefault(require("prop-types")); var _react = _interopRequireWildcard(require("react")); var _reactFela = require("react-fela"); var _click = require("../click"); var _nav = require("../nav"); var _tabNav = require("../tab-nav"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } // TODO: Fix eslint issues the next time this file is edited. /* eslint-disable react-hooks/exhaustive-deps */ const style = _ref => { let { reverseOut, isActiveTab, duration, theme } = _ref; return { minWidth: 50, alignSelf: 'initial', justifyContent: 'center', flexGrow: 0, flexShrink: 0, flexBasis: 'auto', whiteSpace: 'nowrap', paddingTop: theme.baselineGrid * 2, paddingBottom: theme.baselineGrid * 2, paddingRight: theme.baselineGrid * 2, paddingLeft: 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: 300, fontSize: 16, lineHeight: 1.3, fontFamily: theme.fontTypes.NOVUM, boxSizing: 'border-box', borderBottomWidth: duration === 0 ? 3 : 0, borderBottomStyle: 'solid', borderBottomColor: 'transparent', ':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) { const currentOffset = currentItem ? currentItem.offsetLeft : 0; const currentWidth = currentItem ? currentItem.offsetWidth : 0; const newWidth = newItem.offsetWidth; const newOffset = newItem.offsetLeft; const currentDifference = !isRTL ? currentOffset - scrollItem.offsetLeft : scrollItem.offsetWidth - (currentOffset - scrollItem.offsetLeft) - newWidth; const newDifference = !isRTL ? newOffset - scrollItem.offsetLeft : scrollItem.offsetWidth - (newOffset - scrollItem.offsetLeft) - newWidth; return { currentOffset, currentWidth, currentDifference, newWidth, newOffset, newDifference }; } function activateTab(setActive, currentItem, newItem, scrollItem, borderItem, duration, scrollTolerance, isRTL) { const { currentOffset, currentWidth, currentDifference, newOffset, newWidth, newDifference } = calculatePosition(currentItem, newItem, scrollItem, isRTL); const property = isRTL ? 'marginRight' : 'marginLeft'; const toLeft = currentOffset > newOffset; // just a small helper to conditionally apply styles to the border element function setStyle(style, value) { let 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) { const newScrollLeft = newItem.offsetLeft - scrollItem.offsetLeft - scrollItem.scrollLeft; const 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(() => { 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(() => { setStyle('width', newWidth); setStyle(property, newDifference, !isRTL); }, duration * 3 / 2); } } /** * @deprecated */ const TabNavItem = exports.TabNavItem = /*#__PURE__*/_react.default.forwardRef((_ref2, ref) => { let { children, isActive, onClick, ...props } = _ref2; const { active, setActive, scrollRef, borderRef, reverseOut, duration = 0 } = (0, _react.useContext)(_tabNav.TabNavContext); const { theme } = (0, _reactFela.useFela)(); const keyboardNavigationProps = (0, _nav.useKeyboardNavigation)({ ref, onKeyDown: props.onKeyDown }); const itemRef = keyboardNavigationProps.ref; const isRTL = theme.direction !== 'ltr'; const activate = () => activateTab(setActive, active, itemRef.current, scrollRef.current, borderRef.current, duration, theme.baselineGrid * 2.5, isRTL); (0, _react.useEffect)(() => { if (isActive && setActive) { activate(); } }, // also update the active state if isActive is manually updated [isActive, isRTL]); const isActiveTab = active ? active === itemRef.current : isActive; const styleProps = { isActiveTab, reverseOut, duration, theme }; const onClickHandler = e => { if (setActive) { activate(); } if (onClick) { onClick(e); } }; return /*#__PURE__*/_react.default.createElement(_click.Click, (0, _extends2.default)({ role: "tab", "aria-selected": isActiveTab }, props, { onClick: onClickHandler }, keyboardNavigationProps, { extend: [style(styleProps), {}] }), children); }); TabNavItem.displayName = 'TabNavItem'; TabNavItem.propTypes = { children: _propTypes.default.node, /** Indicate if the TabNavItem will open a dropdown menu */ isDropdown: _propTypes.default.bool, /** Indicate if the TabNavItem is in an active state */ isActive: _propTypes.default.bool, /** Onclick function */ onClick: _propTypes.default.func };