@razorpay/blade
Version:
The Design System that powers Razorpay
193 lines (189 loc) • 8.86 kB
JavaScript
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import React__default from 'react';
import styled from 'styled-components';
import '../../../utils/index.js';
import '../../../tokens/global/index.js';
import '../../../utils/metaAttribute/index.js';
import '../../BladeProvider/index.js';
import { useResize } from '../../../utils/useResize.js';
import { useIsomorphicLayoutEffect } from '../../../utils/useIsomorphicLayoutEffect.js';
import '../../Box/BaseBox/index.js';
import { jsxs, jsx } from 'react/jsx-runtime';
import { BaseBox } from '../../Box/BaseBox/BaseBox.web.js';
import { makeSpace } from '../../../utils/makeSpace/makeSpace.js';
import { makeBorderSize } from '../../../utils/makeBorderSize/makeBorderSize.js';
import { size } from '../../../tokens/global/size.js';
import useTheme from '../../BladeProvider/useTheme.js';
import { makeMotionTime } from '../../../utils/makeMotionTime/makeMotionTime.web.js';
import { castWebType } from '../../../utils/platform/castUtils.js';
import { metaAttribute } from '../../../utils/metaAttribute/metaAttribute.web.js';
import { MetaConstants } from '../../../utils/metaAttribute/metaConstants.js';
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 StyledIndicatorWrapper = /*#__PURE__*/styled.div.withConfig({
displayName: "TabNavIndicatorweb__StyledIndicatorWrapper",
componentId: "lqpaj2-0"
})({
pointerEvents: 'none',
position: 'absolute',
bottom: 0,
left: 0,
willChange: 'transform, width, opacity'
});
var StyledTabNavIndicatorLine = /*#__PURE__*/styled(BaseBox).withConfig({
displayName: "TabNavIndicatorweb__StyledTabNavIndicatorLine",
componentId: "lqpaj2-1"
})(function (_ref) {
var theme = _ref.theme;
return {
position: 'relative',
width: '100%',
height: makeSpace(theme.border.width.thinner),
borderTopLeftRadius: makeBorderSize(theme.border.radius.medium),
borderTopRightRadius: makeBorderSize(theme.border.radius.medium)
};
});
var GLOW_OVERFLOW = 32;
var GLOW_HEIGHT = size[56];
var buildGlowMask = function buildGlowMask(width) {
var height = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : GLOW_HEIGHT;
var cx = width / 2;
var rx = width * 0.37;
return "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='".concat(width, "' height='").concat(height, "'%3E%3Cdefs%3E%3Cfilter id='b' filterUnits='userSpaceOnUse' x='0' y='0' width='").concat(width, "' height='").concat(height, "'%3E%3CfeGaussianBlur stdDeviation='40'/%3E%3C/filter%3E%3C/defs%3E%3Cellipse cx='").concat(cx, "' cy='").concat(height, "' rx='").concat(rx, "' ry='60' fill='black' filter='url(%23b)'/%3E%3C/svg%3E\")");
};
var StyledIndicatorGlow = /*#__PURE__*/styled.div.withConfig({
displayName: "TabNavIndicatorweb__StyledIndicatorGlow",
componentId: "lqpaj2-2"
})(function (_ref2) {
var glowColor = _ref2.glowColor,
glowWidth = _ref2.glowWidth,
glowMask = _ref2.glowMask;
var totalWidth = glowWidth + GLOW_OVERFLOW * 2;
return {
position: 'absolute',
bottom: 0,
left: "".concat(-GLOW_OVERFLOW, "px"),
width: "".concat(totalWidth, "px"),
height: "".concat(GLOW_HEIGHT, "px"),
// Tuned visually to blend the glow into the dark TopNav background without overpowering the indicator line
opacity: 0.8,
background: "radial-gradient(50% 100% at 50% 100%, ".concat(glowColor, " 0%, transparent 100%)"),
WebkitMaskImage: glowMask,
WebkitMaskRepeat: 'no-repeat',
WebkitMaskPosition: 'center bottom',
WebkitMaskSize: '100% 100%',
maskImage: glowMask,
maskRepeat: 'no-repeat',
maskPosition: 'center bottom',
maskSize: '100% 100%'
};
});
var ACTIVE_ITEM_SELECTOR = '[data-blade-component="tab-nav-item"][data-active="true"]';
var TabNavIndicator = function TabNavIndicator(_ref3) {
var containerRef = _ref3.containerRef;
var _useTheme = useTheme(),
theme = _useTheme.theme;
var wrapperRef = React__default.useRef(null);
var shouldAnimateRef = React__default.useRef(false);
var _React$useState = React__default.useState(0),
_React$useState2 = _slicedToArray(_React$useState, 2),
activeWidth = _React$useState2[0],
setActiveWidth = _React$useState2[1];
var _React$useState3 = React__default.useState(theme.colors.surface.background.primary.intense),
_React$useState4 = _slicedToArray(_React$useState3, 2),
glowColor = _React$useState4[0],
setGlowColor = _React$useState4[1];
var glowMask = React__default.useMemo(function () {
return buildGlowMask(activeWidth + GLOW_OVERFLOW * 2);
}, [activeWidth]);
var updatePosition = React__default.useCallback(function () {
var _activeItem$dataset$g;
var container = containerRef.current;
var wrapper = wrapperRef.current;
if (!container || !wrapper) return;
var activeItem = container.querySelector(ACTIVE_ITEM_SELECTOR);
if (!activeItem || activeItem.offsetWidth === 0) {
wrapper.style.opacity = '0';
return;
}
var containerRect = container.getBoundingClientRect();
var activeRect = activeItem.getBoundingClientRect();
var x = activeRect.left - containerRect.left;
var duration = shouldAnimateRef.current ? makeMotionTime(theme.motion.duration.gentle) : '0ms';
wrapper.style.transitionDuration = castWebType(duration);
wrapper.style.width = "".concat(activeRect.width, "px");
wrapper.style.transform = "translateX(".concat(x, "px)");
wrapper.style.opacity = '1';
setActiveWidth(activeRect.width);
setGlowColor((_activeItem$dataset$g = activeItem.dataset.glowColor) !== null && _activeItem$dataset$g !== void 0 ? _activeItem$dataset$g : theme.colors.surface.background.primary.intense);
if (!shouldAnimateRef.current) {
requestAnimationFrame(function () {
shouldAnimateRef.current = true;
});
}
}, [containerRef, theme.motion.duration.gentle, theme.colors.surface.background.primary.intense]);
useIsomorphicLayoutEffect(function () {
void updatePosition();
}, [updatePosition]);
// Watch for data-active attribute changes AND child additions/removals so the
// indicator moves when: (a) active tab changes via router, (b) an item moves
// into/out of the "More" overflow (the More button is added to the DOM with
// data-active already set, so a pure attribute observer would miss it).
React__default.useEffect(function () {
var container = containerRef.current;
if (!container) return;
var rafId;
var observer = new MutationObserver(function () {
cancelAnimationFrame(rafId);
rafId = requestAnimationFrame(function () {
void updatePosition();
});
});
observer.observe(container, {
attributes: true,
attributeFilter: ['data-active'],
childList: true,
subtree: true
});
return function () {
cancelAnimationFrame(rafId);
observer.disconnect();
};
}, [containerRef, updatePosition]);
React__default.useEffect(function () {
if ('fonts' in document) {
try {
void document.fonts.ready.then(function () {
updatePosition();
});
} catch (_unused) {
/* empty */
}
}
}, [updatePosition]);
useResize(containerRef, updatePosition);
return /*#__PURE__*/jsxs(StyledIndicatorWrapper, _objectSpread(_objectSpread({
ref: wrapperRef,
style: {
transitionProperty: 'transform, width, opacity',
transitionTimingFunction: castWebType(theme.motion.easing.standard),
transitionDuration: '0ms',
opacity: 0
}
}, metaAttribute({
name: MetaConstants.TabNavIndicator
})), {}, {
children: [activeWidth > 0 && /*#__PURE__*/jsx(StyledIndicatorGlow, {
glowColor: glowColor,
glowWidth: activeWidth,
glowMask: glowMask
}), /*#__PURE__*/jsx(StyledTabNavIndicatorLine, {
style: {
background: "linear-gradient(90deg, transparent 0%, ".concat(theme.colors.surface.icon.staticWhite.normal, " 20%, ").concat(theme.colors.surface.icon.staticWhite.normal, " 80.29%, transparent 100%)")
}
})]
}));
};
export { TabNavIndicator };
//# sourceMappingURL=TabNavIndicator.web.js.map