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