UNPKG

@awsui/components-react

Version:

On July 19th, 2022, we launched [Cloudscape Design System](https://cloudscape.design). Cloudscape is an evolution of AWS-UI. It consists of user interface guidelines, front-end components, design resources, and development tools for building intuitive, en

80 lines 4.81 kB
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React, { forwardRef, useCallback, useImperativeHandle, useRef } from 'react'; import { useContainerQuery } from '@awsui/component-toolkit'; import { useMergeRefs, useUniqueId } from '@awsui/component-toolkit/internal'; import OptionsList from '../../internal/components/options-list'; import { useVirtual } from '../../internal/hooks/use-virtual'; import { renderOptions } from '../utils/render-options'; import customScrollToIndex from '../utils/scroll-to-index'; import { fallbackItemHeight } from './common'; import styles from './styles.css.js'; const VirtualList = (props, ref) => { return props.menuProps.open ? React.createElement(VirtualListOpen, { ...props, ref: ref }) : React.createElement(VirtualListClosed, { ...props, ref: ref }); }; const VirtualListOpen = forwardRef(({ menuProps, getOptionProps, filteredOptions, filteringValue, highlightType, checkboxes, hasDropdownStatus, listBottom, useInteractiveGroups, screenReaderContent, firstOptionSticky, }, ref) => { // update component, when it gets wider or narrower to reposition items const [width, menuMeasureRef] = useContainerQuery(rect => ({ inner: rect.contentBoxWidth, outer: rect.borderBoxWidth }), []); const menuRefObject = useRef(null); const menuRef = useMergeRefs(menuMeasureRef, menuRefObject, menuProps.ref); const previousHighlightedIndex = useRef(); const { virtualItems, totalSize, scrollToIndex } = useVirtual({ items: filteredOptions, parentRef: menuRefObject, // estimateSize is a dependency of measurements memo. We update it to force full recalculation // when the height of any option could have changed: // 1: because the component got resized (width property got updated) // 2: because the option changed its content (filteringValue property controls the highlight and the visibility of hidden tags) // eslint-disable-next-line react-hooks/exhaustive-deps estimateSize: useCallback(() => fallbackItemHeight, [width === null || width === void 0 ? void 0 : width.inner, filteringValue]), firstItemSticky: firstOptionSticky, }); useImperativeHandle(ref, () => (index) => { if (highlightType.moveFocus) { const movingUp = previousHighlightedIndex.current !== undefined && index < previousHighlightedIndex.current; if (firstOptionSticky && movingUp && index !== 0 && menuRefObject.current) { // React-Virtual v2 does not offer a proper way to handle sticky elements when scrolling, // so until we upgrade to v3, use our own scroll implementation // to prevent newly highlighted element from being covered by the sticky element // when moving the highlight upwards in the list. // Scrolling behavior is covered by integration tests. // istanbul ignore next customScrollToIndex({ index, menuEl: menuRefObject === null || menuRefObject === void 0 ? void 0 : menuRefObject.current, }); } else { scrollToIndex(index); } } previousHighlightedIndex.current = index; }, [firstOptionSticky, highlightType.moveFocus, scrollToIndex]); const stickySize = firstOptionSticky ? virtualItems[0].size : 0; const withScrollbar = !!width && width.inner < width.outer; const idPrefix = useUniqueId('select-list-'); const finalOptions = renderOptions({ options: virtualItems.map(({ index }) => filteredOptions[index]), getOptionProps, filteringValue, highlightType, idPrefix, checkboxes, hasDropdownStatus, virtualItems, useInteractiveGroups, screenReaderContent, firstOptionSticky, withScrollbar, }); return (React.createElement(OptionsList, { ...menuProps, stickyItemBlockSize: stickySize, ref: menuRef }, finalOptions, React.createElement("div", { "aria-hidden": "true", key: "total-size", className: styles['layout-strut'], style: { height: totalSize - stickySize } }), listBottom ? (React.createElement("div", { role: "option", className: styles['list-bottom'] }, listBottom)) : null)); }); const VirtualListClosed = forwardRef(({ menuProps, listBottom }, ref) => { useImperativeHandle(ref, () => () => { }, []); return (React.createElement(OptionsList, { ...menuProps, ref: menuProps.ref }, listBottom ? (React.createElement("div", { role: "option", className: styles['list-bottom'] }, listBottom)) : null)); }); export default forwardRef(VirtualList); //# sourceMappingURL=virtual-list.js.map