UNPKG

@razorpay/blade

Version:

The Design System that powers Razorpay

399 lines (395 loc) 16.9 kB
import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties'; import _defineProperty from '@babel/runtime/helpers/defineProperty'; import 'react'; import styled from 'styled-components'; import { useFloating, FloatingPortal, FloatingFocusManager } from '@floating-ui/react'; import { useSideNav, useNavLink, NavLinkContext } from '../SideNavContext.js'; import { classes, NAV_ITEM_HEIGHT, getNavItemTransition } from '../tokens.js'; import '../../Box/index.js'; import '../../../utils/index.js'; import '../../Typography/BaseText/index.js'; import '../../Icons/index.js'; import '../../Box/BaseBox/index.js'; import { useCollapsible } from '../../Collapsible/CollapsibleContext.js'; import '../../Collapsible/index.js'; import '../../../utils/makeAccessible/index.js'; import { useFirstRender } from '../../../utils/useFirstRender.js'; import '../../../utils/getFocusRingStyles/index.js'; import { useIsomorphicLayoutEffect } from '../../../utils/useIsomorphicLayoutEffect.js'; import '../../../utils/logger/index.js'; import '../../../utils/makeAnalyticsAttribute/index.js'; import '../../Typography/index.js'; import { TooltipifyComponent } from '../../../utils/TooltipifyComponent.js'; import { jsxs, jsx, Fragment } from 'react/jsx-runtime'; import { BaseBox } from '../../Box/BaseBox/BaseBox.web.js'; import { makeSize } from '../../../utils/makeSize/makeSize.js'; import { makeSpace } from '../../../utils/makeSpace/makeSpace.js'; import { makeBorderSize } from '../../../utils/makeBorderSize/makeBorderSize.js'; import { getFocusRingStyles } from '../../../utils/getFocusRingStyles/getFocusRingStyles.web.js'; import { Box } from '../../Box/Box.js'; import { BaseText } from '../../Typography/BaseText/BaseText.web.js'; import { Text } from '../../Typography/Text/Text.js'; import { makeAccessible } from '../../../utils/makeAccessible/makeAccessible.web.js'; import ChevronUpIcon from '../../Icons/ChevronUpIcon/ChevronUpIcon.js'; import ChevronDownIcon from '../../Icons/ChevronDownIcon/ChevronDownIcon.js'; import { throwBladeError } from '../../../utils/logger/logger.js'; import { Collapsible } from '../../Collapsible/Collapsible.js'; import { CollapsibleBody } from '../../Collapsible/CollapsibleBody.js'; import { makeAnalyticsAttribute } from '../../../utils/makeAnalyticsAttribute/makeAnalyticsAttribute.js'; import ChevronRightIcon from '../../Icons/ChevronRightIcon/ChevronRightIcon.js'; var _excluded = ["title", "description", "href", "children", "titleSuffix", "trailing", "isActive", "icon", "tooltip", "as", "target", "onClick"]; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } var SHOW_ON_LINK_HOVER = classes.SHOW_ON_LINK_HOVER, HIDE_WHEN_COLLAPSED = classes.HIDE_WHEN_COLLAPSED, STYLED_NAV_LINK = classes.STYLED_NAV_LINK; var StyledNavLinkContainer = /*#__PURE__*/styled(BaseBox).withConfig({ displayName: "SideNavLinkweb__StyledNavLinkContainer", componentId: "sc-1ypwkeo-0" })(function (props) { return _defineProperty(_defineProperty(_defineProperty({ width: '100%' }, ".".concat(SHOW_ON_LINK_HOVER), { opacity: 0, '&:focus-within, &:focus-visible': { opacity: 1 } }), '&:hover', _defineProperty(_defineProperty({}, ".".concat(SHOW_ON_LINK_HOVER), { opacity: 1 }), ".".concat(STYLED_NAV_LINK), { color: props.theme.colors.interactive.text.gray.normal, backgroundColor: props.theme.colors.interactive.background.gray["default"] })), ".".concat(STYLED_NAV_LINK), { position: 'relative', display: 'flex', flexDirection: 'row', alignItems: 'center', height: props.$hasDescription ? undefined : makeSize(NAV_ITEM_HEIGHT), width: '100%', textDecoration: 'none', overflow: 'hidden', flexWrap: 'nowrap', cursor: 'pointer', padding: "".concat(makeSpace(props.theme.spacing[props.$hasDescription ? 3 : 0]), " ").concat(makeSpace(props.theme.spacing[4])), margin: "".concat(makeSpace(props.theme.spacing[1]), " ").concat(makeSpace(props.theme.spacing[0])), color: props.theme.colors.interactive.text.gray.subtle, borderRadius: props.theme.border.radius.medium, borderWidth: makeBorderSize(props.theme.border.width.none), backgroundColor: props.theme.colors.transparent, transition: getNavItemTransition(props.theme), '&[aria-current]': { color: props.theme.colors.interactive.text.primary.subtle, backgroundColor: props.theme.colors.interactive.background.primary.faded }, '&[aria-current]:hover': { color: props.theme.colors.interactive.text.primary.normal, backgroundColor: props.theme.colors.interactive.background.primary.fadedHighlighted }, '&:focus-visible': _objectSpread({}, getFocusRingStyles({ theme: props.theme })) }); }); var NavLinkIconTitle = function NavLinkIconTitle(_ref2) { var Icon = _ref2.icon, title = _ref2.title, description = _ref2.description, titleSuffix = _ref2.titleSuffix, isActive = _ref2.isActive, trailing = _ref2.trailing, isL1Item = _ref2.isL1Item; return /*#__PURE__*/jsxs(Box, { width: "100%", textAlign: "left", children: [/*#__PURE__*/jsxs(Box, { display: "flex", justifyContent: "space-between", width: "100%", children: [/*#__PURE__*/jsxs(Box, { display: "flex", flexDirection: "row", gap: "spacing.3", alignItems: "center", children: [Icon ? /*#__PURE__*/jsx(BaseBox, { display: "flex", flexDirection: "row", alignItems: "center", children: /*#__PURE__*/jsx(Icon, { size: "medium", color: "currentColor" }) }) : null, /*#__PURE__*/jsx(BaseText, { truncateAfterLines: 1, color: "currentColor", fontWeight: "medium", fontSize: 100, lineHeight: 100, as: "p", className: isL1Item ? HIDE_WHEN_COLLAPSED : '', children: title }), titleSuffix ? /*#__PURE__*/jsx(BaseBox, { display: "flex", alignItems: "center", children: titleSuffix }) : null] }), /*#__PURE__*/jsx(Box, { display: "flex", alignItems: "center", children: trailing })] }), !isL1Item && description ? /*#__PURE__*/jsx(Text, { size: "small", marginLeft: "spacing.7", textAlign: "left", weight: "medium", color: isActive ? 'interactive.text.primary.muted' : 'interactive.text.gray.muted', truncateAfterLines: 1, children: description }) : null] }); }; var L3Trigger = function L3Trigger(_ref3) { var title = _ref3.title, description = _ref3.description, icon = _ref3.icon, as = _ref3.as, href = _ref3.href, target = _ref3.target, titleSuffix = _ref3.titleSuffix, tooltip = _ref3.tooltip, onClick = _ref3.onClick; var _useCollapsible = useCollapsible(), onExpandChange = _useCollapsible.onExpandChange, isExpanded = _useCollapsible.isExpanded, collapsibleBodyId = _useCollapsible.collapsibleBodyId; var toggleCollapse = function toggleCollapse(e) { onClick === null || onClick === void 0 ? void 0 : onClick(e); onExpandChange(!isExpanded); }; var iconProps = { size: 'medium', color: 'currentColor' }; return /*#__PURE__*/jsx(TooltipifyComponent, { tooltip: tooltip, children: /*#__PURE__*/jsx(StyledNavLinkContainer, { $hasDescription: Boolean(description), children: /*#__PURE__*/jsx(BaseBox, _objectSpread(_objectSpread({ className: STYLED_NAV_LINK, as: href ? as : 'button', to: href, target: target, onClick: function onClick(e) { return toggleCollapse(e); } }, makeAccessible({ expanded: isExpanded, controls: collapsibleBodyId })), {}, { children: /*#__PURE__*/jsx(NavLinkIconTitle, { title: title, description: description, icon: icon, isL1Item: false, titleSuffix: titleSuffix, trailing: isExpanded ? /*#__PURE__*/jsx(ChevronUpIcon, _objectSpread({}, iconProps)) : /*#__PURE__*/jsx(ChevronDownIcon, _objectSpread({}, iconProps)) }) })) }) }); }; /** * This is the curved line that appears when you select L3 item */ var CurvedVerticalLine = /*#__PURE__*/styled(BaseBox).withConfig({ displayName: "SideNavLinkweb__CurvedVerticalLine", componentId: "sc-1ypwkeo-1" })(function (props) { var _props$theme = props.theme, colors = _props$theme.colors, border = _props$theme.border, spacing = _props$theme.spacing; return { borderWidth: makeBorderSize(props.theme.border.width.thin), borderColor: "".concat(colors.transparent, " ").concat(colors.transparent, " ").concat(colors.surface.border.primary.muted, " ").concat(colors.surface.border.primary.muted), borderStyle: 'solid', borderRadius: "".concat(makeBorderSize(border.radius.none), " ").concat(makeBorderSize(border.radius.none), " ").concat(makeBorderSize(border.radius.none), " ").concat(makeBorderSize(border.radius.medium)), // We set veritical line infinitely tall (full size of screen) and then hide the overflowing part from top height: '100vh', position: 'absolute', // We want the active line to be positioned in the middle of item's height thus divide by 2 top: "calc(-100vh + ".concat(makeSize(NAV_ITEM_HEIGHT / 2), ")"), width: makeSpace(spacing[3]), left: makeSpace(-spacing[3]) }; }); var SideNavLink = function SideNavLink(_ref4) { var title = _ref4.title, description = _ref4.description, href = _ref4.href, children = _ref4.children, titleSuffix = _ref4.titleSuffix, trailing = _ref4.trailing, isActive = _ref4.isActive, icon = _ref4.icon, tooltip = _ref4.tooltip, as = _ref4.as, target = _ref4.target, _onClick = _ref4.onClick, rest = _objectWithoutProperties(_ref4, _excluded); var _useSideNav = useSideNav(), l2PortalContainerRef = _useSideNav.l2PortalContainerRef, onLinkActiveChange = _useSideNav.onLinkActiveChange, closeMobileNav = _useSideNav.closeMobileNav, isL1Collapsed = _useSideNav.isL1Collapsed, setIsL1Collapsed = _useSideNav.setIsL1Collapsed; var _useNavLink = useNavLink(), _prevLevel = _useNavLink.level; var prevLevel = _prevLevel !== null && _prevLevel !== void 0 ? _prevLevel : 0; var currentLevel = prevLevel + 1; var isL2Trigger = Boolean(children) && currentLevel === 1; var isL3Trigger = Boolean(children) && currentLevel === 2; if (false) { if (Boolean(children) && currentLevel >= 3) { throwBladeError({ message: 'SideNav only supports nesting upto L3 but L4 nesting was found. Check the nesting of your SideNavLevel items', moduleName: 'SideNavLink' }); } if (currentLevel === 1 && Boolean(description)) { throwBladeError({ message: 'Description is not supported for L1 items', moduleName: 'SideNavLink' }); } } var isFirstRender = useFirstRender(); var _useFloating = useFloating({ open: isActive }), refs = _useFloating.refs, context = _useFloating.context; useIsomorphicLayoutEffect(function () { onLinkActiveChange === null || onLinkActiveChange === void 0 ? void 0 : onLinkActiveChange({ level: currentLevel, title: title, isActive: Boolean(isActive), isL2Trigger: isL2Trigger, isFirstRender: isFirstRender }); // eslint-disable-next-line react-hooks/exhaustive-deps }, [isActive]); return /*#__PURE__*/jsx(NavLinkContext.Provider, { value: { level: currentLevel, title: title }, children: isL3Trigger ? /*#__PURE__*/jsxs(Collapsible, { defaultIsExpanded: isActive, _dangerouslyDisableValidations: true, _shouldApplyWidthRestrictions: false, children: [/*#__PURE__*/jsx(L3Trigger, { title: title, description: description, icon: icon, as: as, href: href, titleSuffix: titleSuffix, onClick: _onClick }), /*#__PURE__*/jsx(CollapsibleBody, { width: "100%", _hasMargin: false, children: /*#__PURE__*/jsx(Box, { position: "relative", children: children }) })] }) : /*#__PURE__*/jsxs(Fragment, { children: [/*#__PURE__*/jsxs(StyledNavLinkContainer, { $hasDescription: currentLevel !== 1 && Boolean(description), position: "relative", children: [/*#__PURE__*/jsx(TooltipifyComponent, { tooltip: tooltip, children: /*#__PURE__*/jsxs(BaseBox, _objectSpread(_objectSpread({ className: STYLED_NAV_LINK, as: as !== null && as !== void 0 ? as : 'a', to: href, href: as ? undefined : href, target: target, ref: refs.setReference, onClick: function onClick(e) { // Close the mobile nav when item is clicked and its not trigger for next menu if (!isL2Trigger) { closeMobileNav === null || closeMobileNav === void 0 ? void 0 : closeMobileNav(); } if (isActive) { onLinkActiveChange === null || onLinkActiveChange === void 0 ? void 0 : onLinkActiveChange({ level: currentLevel, title: title, isActive: Boolean(isActive), isL2Trigger: isL2Trigger, isFirstRender: false }); } _onClick === null || _onClick === void 0 ? void 0 : _onClick(e); }, onFocus: function onFocus(e) { var _e$target; // FloatinFocusManager by default focusses on last clicked element when you move to different tab and come back to the original tab // Which can make L1 to expand when tabs / windows are changed // Adding focus-visible check ensures this behaviour of closing menus is only applicable when there is visible focus ring on it (while tabbing) var hasFocusRing = (_e$target = e.target) === null || _e$target === void 0 ? void 0 : _e$target.matches(':focus-visible'); if (isL1Collapsed && currentLevel === 1 && hasFocusRing) { setIsL1Collapsed === null || setIsL1Collapsed === void 0 ? void 0 : setIsL1Collapsed(false); } }, "aria-current": isActive ? 'page' : undefined, "data-level": currentLevel, "data-l2trigger": isL2Trigger }, makeAnalyticsAttribute(rest)), {}, { children: [/*#__PURE__*/jsx(NavLinkIconTitle, { icon: icon, title: title, description: description, isActive: isActive, isL1Item: currentLevel === 1, titleSuffix: titleSuffix }), isL2Trigger ? /*#__PURE__*/jsx(BaseBox, { className: HIDE_WHEN_COLLAPSED, display: "flex", alignItems: "center", children: /*#__PURE__*/jsx(ChevronRightIcon, { size: "medium", color: "currentColor" }) }) : null] })) }), trailing && !isL2Trigger ? /*#__PURE__*/jsx(BaseBox, { position: "absolute", top: "spacing.0", right: "spacing.2", height: "100%", display: "flex", alignItems: "center", className: "".concat(HIDE_WHEN_COLLAPSED, " ").concat(SHOW_ON_LINK_HOVER), children: trailing }) : null, currentLevel === 3 && isActive ? /*#__PURE__*/jsx(CurvedVerticalLine, {}) : null] }), children ? /*#__PURE__*/jsx(FloatingPortal, { root: l2PortalContainerRef, children: isActive && isL1Collapsed ? /*#__PURE__*/jsx(FloatingFocusManager, { modal: false, context: context, initialFocus: -1, returnFocus: true, children: /*#__PURE__*/jsx(BaseBox, { ref: refs.setFloating, height: "100%", children: children }) }) : null }) : null] }) }); }; export { SideNavLink }; //# sourceMappingURL=SideNavLink.web.js.map