UNPKG

@razorpay/blade

Version:

The Design System that powers Razorpay

285 lines (278 loc) 14.8 kB
import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray'; import _defineProperty from '@babel/runtime/helpers/defineProperty'; import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties'; import React__default, { useCallback } from 'react'; import { VariableSizeList } from 'react-window'; import { StyledListBoxWrapper } from './styles/StyledListBoxWrapper.web.js'; import { getActionListPadding, actionListMaxHeight } from './styles/getBaseListBoxWrapperStyles.js'; import { componentIds } from './componentIds.js'; import { ActionListSectionTitle } from './ActionListItem.js'; import { useBottomSheetContext } from '../BottomSheet/BottomSheetContext.js'; import '../../utils/assignWithoutSideEffects/index.js'; import '../../utils/makeAccessible/index.js'; import '../../utils/makeAnalyticsAttribute/index.js'; import { useIsMobile } from '../../utils/useIsMobile.js'; import { actionListSectionTitleHeight, actionListDividerHeight, getActionListItemHeight } from '../BaseMenu/BaseMenuItem/tokens.js'; import '../../utils/index.js'; import { useDropdown } from '../Dropdown/useDropdown.js'; import { dropdownComponentIds } from '../Dropdown/dropdownComponentIds.js'; import '../../utils/isValidAllowedChildren/index.js'; import '../Divider/index.js'; import { jsx } from 'react/jsx-runtime'; import { makeAccessible } from '../../utils/makeAccessible/makeAccessible.web.js'; import { makeAnalyticsAttribute } from '../../utils/makeAnalyticsAttribute/makeAnalyticsAttribute.js'; import { assignWithoutSideEffects } from '../../utils/assignWithoutSideEffects/assignWithoutSideEffects.js'; import { getComponentId } from '../../utils/isValidAllowedChildren/isValidAllowedChildren.js'; import { Divider } from '../Divider/Divider.js'; import useTheme from '../BladeProvider/useTheme.js'; var _excluded = ["childrenWithId", "actionListItemWrapperRole", "isMultiSelectable"], _excluded2 = ["childrenWithId", "actionListItemWrapperRole", "isMultiSelectable"]; 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 _ActionListBox = /*#__PURE__*/React__default.forwardRef(function (_ref, ref) { var childrenWithId = _ref.childrenWithId, actionListItemWrapperRole = _ref.actionListItemWrapperRole, isMultiSelectable = _ref.isMultiSelectable, rest = _objectWithoutProperties(_ref, _excluded); var _useBottomSheetContex = useBottomSheetContext(), isInBottomSheet = _useBottomSheetContex.isInBottomSheet; return /*#__PURE__*/jsx(StyledListBoxWrapper, _objectSpread(_objectSpread(_objectSpread({ isInBottomSheet: isInBottomSheet, ref: ref }, makeAccessible({ role: actionListItemWrapperRole, multiSelectable: actionListItemWrapperRole === 'listbox' ? isMultiSelectable : undefined })), makeAnalyticsAttribute(rest)), {}, { children: childrenWithId })); }); var ActionListBox = /*#__PURE__*/assignWithoutSideEffects( /*#__PURE__*/React__default.memo(_ActionListBox), { displayName: 'ActionListBox' }); /** * get the height of the item based on the componentId */ var getItemHeight = function getItemHeight(_ref2) { var _itemData$index; var index = _ref2.index, itemData = _ref2.itemData, actionListItemHeight = _ref2.actionListItemHeight; if (getComponentId(itemData[index]) === componentIds.ActionListItem) { return actionListItemHeight; } if (getComponentId(itemData[index]) === componentIds.ActionListSectionTitle) { return actionListSectionTitleHeight; } // @ts-expect-error: key does exist if ((_itemData$index = itemData[index]) !== null && _itemData$index !== void 0 && _itemData$index.key.includes('divider')) { return actionListDividerHeight; } return 0; }; /** * Returns the height of item and height of container based on theme and device */ var getVirtualItemParams = function getVirtualItemParams(_ref3) { var theme = _ref3.theme, isMobile = _ref3.isMobile, itemCount = _ref3.itemCount, itemData = _ref3.itemData; var itemHeightResponsive = getActionListItemHeight(theme); var actionListPadding = getActionListPadding(theme); var actionListItemHeight = isMobile ? itemHeightResponsive.itemHeightMobile : itemHeightResponsive.itemHeightDesktop; var shouldCalculateMinimumHeight = itemCount <= 10; var actionListBoxHeight = actionListMaxHeight - actionListPadding * 2; var actionListBoxMinHeight = shouldCalculateMinimumHeight ? itemData.reduce(function (acc, _, index) { var itemHeight = getItemHeight({ index: index, itemData: itemData, actionListItemHeight: actionListItemHeight }); return acc + itemHeight; }, 0) : actionListBoxHeight; var finalActionListBoxHeight = Math.min(actionListBoxHeight, actionListBoxMinHeight); return { actionListItemHeight: actionListItemHeight, actionListBoxHeight: finalActionListBoxHeight }; }; /** * Takes the children (ActionListItem) and returns the filtered items based on `filteredValues` state */ var useFilteredItems = function useFilteredItems(children) { var childrenArray = React__default.Children.toArray(children); // Convert children to an array var _useDropdown = useDropdown(), filteredValues = _useDropdown.filteredValues, hasAutoCompleteInHeader = _useDropdown.hasAutoCompleteInHeader, dropdownTriggerer = _useDropdown.dropdownTriggerer; var items = React__default.useMemo(function () { var hasAutoComplete = hasAutoCompleteInHeader || dropdownTriggerer === dropdownComponentIds.triggers.AutoComplete; if (!hasAutoComplete) { return childrenArray; } var filteredItems = childrenArray.reduce(function (acc, item, index) { if (getComponentId(item) === componentIds.ActionListSection) { var sectionTitle = /*#__PURE__*/jsx(ActionListSectionTitle, { // @ts-expect-error: props does exist title: item === null || item === void 0 ? void 0 : item.props.title, isInsideVirtualizedList: true }, index); // @ts-expect-error: props does exist var sectionChildren = item === null || item === void 0 ? void 0 : item.props.children; var divider = index !== childrenArray.length - 1 ? /*#__PURE__*/jsx(Divider, { marginX: "spacing.3", marginY: "spacing.1" }, "divider-".concat(index)) : null; var filteredSectionChildren = sectionChildren.filter(function (item) { return filteredValues.includes(item.props.value); }); if (filteredSectionChildren.length !== 0) { acc.push.apply(acc, [sectionTitle].concat(_toConsumableArray(filteredSectionChildren), [divider])); } } else { // @ts-expect-error: props does exist var value = item === null || item === void 0 ? void 0 : item.props.value; if (filteredValues.includes(value)) { acc.push(item); } } return acc; }, []); return filteredItems; }, [filteredValues, hasAutoCompleteInHeader, dropdownTriggerer, childrenArray]); return { itemData: items, itemCount: items.length }; }; var VirtualListItem = /*#__PURE__*/React__default.memo(function (_ref4) { var index = _ref4.index, style = _ref4.style, data = _ref4.data, onVirtualizedFocus = _ref4.onVirtualizedFocus; var currentItem = data[index]; if ( /*#__PURE__*/React__default.isValidElement(currentItem) && getComponentId(currentItem) === componentIds.ActionListItem) { // Clone the element passed via `data` and add the `_virtualizedIndex` prop var elementWithIndex = /*#__PURE__*/React__default.cloneElement(currentItem, { _virtualizedIndex: index, _onVirtualizedFocus: onVirtualizedFocus }); return /*#__PURE__*/jsx("div", { style: style, children: elementWithIndex }); } return /*#__PURE__*/jsx("div", { style: style, children: data[index] }); }, function (prevProps, nextProps) { // Custom comparison function to determine if component should update return prevProps.index === nextProps.index && prevProps.style === nextProps.style && prevProps.data[prevProps.index] === nextProps.data[nextProps.index]; }); var _ActionListVirtualizedBox = /*#__PURE__*/React__default.forwardRef(function (_ref5, ref) { var childrenWithId = _ref5.childrenWithId, actionListItemWrapperRole = _ref5.actionListItemWrapperRole, isMultiSelectable = _ref5.isMultiSelectable, rest = _objectWithoutProperties(_ref5, _excluded2); var virtualizedListRef = React__default.useRef(null); var _React$useState = React__default.useState(0), _React$useState2 = _slicedToArray(_React$useState, 2), visibleStartIndex = _React$useState2[0], setVisibleStartIndex = _React$useState2[1]; var _React$useState3 = React__default.useState(0), _React$useState4 = _slicedToArray(_React$useState3, 2), visibleStopIndex = _React$useState4[0], setVisibleStopIndex = _React$useState4[1]; var items = React__default.Children.toArray(childrenWithId); // Convert children to an array var _useBottomSheetContex2 = useBottomSheetContext(), isInBottomSheet = _useBottomSheetContex2.isInBottomSheet; var _useFilteredItems = useFilteredItems(items), itemData = _useFilteredItems.itemData, itemCount = _useFilteredItems.itemCount; var isMobile = useIsMobile(); var _useTheme = useTheme(), theme = _useTheme.theme; var _React$useMemo = React__default.useMemo(function () { return getVirtualItemParams({ theme: theme, isMobile: isMobile, itemCount: itemCount, itemData: itemData }); }, // eslint-disable-next-line react-hooks/exhaustive-deps [theme.name, isMobile, itemCount, itemData]), actionListItemHeight = _React$useMemo.actionListItemHeight, actionListBoxHeight = _React$useMemo.actionListBoxHeight; React__default.useEffect(function () { var _virtualizedListRef$c, _virtualizedListRef$c2; virtualizedListRef === null || virtualizedListRef === void 0 ? void 0 : (_virtualizedListRef$c = virtualizedListRef.current) === null || _virtualizedListRef$c === void 0 ? void 0 : _virtualizedListRef$c.resetAfterIndex(0); virtualizedListRef === null || virtualizedListRef === void 0 ? void 0 : (_virtualizedListRef$c2 = virtualizedListRef.current) === null || _virtualizedListRef$c2 === void 0 ? void 0 : _virtualizedListRef$c2.scrollToItem(0); }, [itemCount]); return /*#__PURE__*/jsx(StyledListBoxWrapper, _objectSpread(_objectSpread(_objectSpread({ isInBottomSheet: isInBottomSheet // in case of virtualized list, we only render visible items. so css will hide divider for every last item visible. instead of hiding the last divider of the list. , ref: ref }, makeAccessible({ role: actionListItemWrapperRole, multiSelectable: actionListItemWrapperRole === 'listbox' ? isMultiSelectable : undefined })), makeAnalyticsAttribute(rest)), {}, { children: /*#__PURE__*/jsx(VariableSizeList, { ref: virtualizedListRef, height: actionListBoxHeight, width: "100%", itemSize: function itemSize(index) { return getItemHeight({ index: index, itemData: itemData, actionListItemHeight: actionListItemHeight }); }, itemCount: itemCount, itemData: itemData, itemKey: function itemKey(index) { var _ref6, _itemData$index$props, _itemData$index2, _itemData$index3, _itemData$index4; return (// @ts-expect-error: props does exist (_ref6 = (_itemData$index$props = (_itemData$index2 = itemData[index]) === null || _itemData$index2 === void 0 ? void 0 : _itemData$index2.props.value) !== null && _itemData$index$props !== void 0 ? _itemData$index$props : // @ts-expect-error: props does exist (_itemData$index3 = itemData[index]) === null || _itemData$index3 === void 0 ? void 0 : _itemData$index3.props.title) !== null && _ref6 !== void 0 ? _ref6 : // @ts-expect-error: props does exist (_itemData$index4 = itemData[index]) === null || _itemData$index4 === void 0 ? void 0 : _itemData$index4.props.key ); }, onItemsRendered: function onItemsRendered(_ref7) { var visibleStartIndex = _ref7.visibleStartIndex, visibleStopIndex = _ref7.visibleStopIndex; setVisibleStartIndex(visibleStartIndex); setVisibleStopIndex(visibleStopIndex); }, children: useCallback(function (_ref8) { var index = _ref8.index, style = _ref8.style, data = _ref8.data; return /*#__PURE__*/jsx(VirtualListItem, { index: index, style: style, data: data, onVirtualizedFocus: function onVirtualizedFocus(index) { var _virtualizedListRef$c3, _virtualizedListRef$c4; // We need scroll Direction to determine the index to focus var scrollDirection = Math.round((visibleStartIndex + visibleStopIndex) / 2) > index ? 'top' : 'bottom'; virtualizedListRef === null || virtualizedListRef === void 0 ? void 0 : (_virtualizedListRef$c3 = virtualizedListRef.current) === null || _virtualizedListRef$c3 === void 0 ? void 0 : _virtualizedListRef$c3.resetAfterIndex(0); /** * we are scrolling to the item which is 3 items away from the current item. * since we can have 2 item sectoin header and divider which are not focusable. */ virtualizedListRef === null || virtualizedListRef === void 0 ? void 0 : (_virtualizedListRef$c4 = virtualizedListRef.current) === null || _virtualizedListRef$c4 === void 0 ? void 0 : _virtualizedListRef$c4.scrollToItem(index + (scrollDirection === 'top' ? -3 : 3), 'smart'); } }); }, [visibleStartIndex, visibleStopIndex]) }) })); }); var ActionListVirtualizedBox = /*#__PURE__*/assignWithoutSideEffects( /*#__PURE__*/React__default.memo(_ActionListVirtualizedBox), { displayName: 'ActionListVirtualizedBox' }); export { ActionListBox, ActionListVirtualizedBox }; //# sourceMappingURL=ActionListBox.web.js.map