@razorpay/blade
Version:
The Design System that powers Razorpay
399 lines (395 loc) • 16.9 kB
JavaScript
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