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

125 lines • 9.18 kB
import { __rest } from "tslib"; // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React, { useEffect, useRef, useState } from 'react'; import clsx from 'clsx'; import { warnOnce } from '@awsui/component-toolkit/internal'; import { useInternalI18n } from '../i18n/context.js'; import { getBaseProps } from '../internal/base-component'; import Dropdown from '../internal/components/dropdown'; import DropdownFooter from '../internal/components/dropdown-footer'; import { useDropdownStatus } from '../internal/components/dropdown-status'; import { prepareOptions } from '../internal/components/option/utils/prepare-options'; import { useFormFieldContext } from '../internal/context/form-field-context'; import { fireNonCancelableEvent } from '../internal/events'; import checkControlled from '../internal/hooks/check-controlled'; import { useMergeRefs } from '../internal/hooks/use-merge-refs'; import { useUniqueId } from '../internal/hooks/use-unique-id'; import { joinStrings } from '../internal/utils/strings/join-strings.js'; import Filter from './parts/filter'; import PlainList from './parts/plain-list'; import Trigger from './parts/trigger'; import VirtualList from './parts/virtual-list'; import { checkOptionValueField } from './utils/check-option-value-field'; import { useAnnouncement } from './utils/use-announcement'; import { useLoadItems } from './utils/use-load-items'; import { useNativeSearch } from './utils/use-native-search'; import { useSelect } from './utils/use-select'; import styles from './styles.css.js'; const InternalSelect = React.forwardRef((_a, externalRef) => { var _b; var { options, filteringType = 'none', filteringPlaceholder, filteringAriaLabel, filteringClearAriaLabel, filteringResultsText, inlineLabelText, ariaRequired, placeholder, disabled, readOnly, ariaLabel, statusType = 'finished', empty, loadingText, finishedText, errorText, noMatch, triggerVariant = 'label', renderHighlightedAriaLive, selectedOption, onBlur, onFocus, onLoadItems, onChange, virtualScroll, expandToViewport, autoFocus, __inFilteringToken, __internalRootRef = null } = _a, restProps = __rest(_a, ["options", "filteringType", "filteringPlaceholder", "filteringAriaLabel", "filteringClearAriaLabel", "filteringResultsText", "inlineLabelText", "ariaRequired", "placeholder", "disabled", "readOnly", "ariaLabel", "statusType", "empty", "loadingText", "finishedText", "errorText", "noMatch", "triggerVariant", "renderHighlightedAriaLive", "selectedOption", "onBlur", "onFocus", "onLoadItems", "onChange", "virtualScroll", "expandToViewport", "autoFocus", "__inFilteringToken", "__internalRootRef"]); const baseProps = getBaseProps(restProps); const formFieldContext = useFormFieldContext(restProps); const i18n = useInternalI18n('select'); const errorIconAriaLabel = i18n('errorIconAriaLabel', restProps.errorIconAriaLabel); const selectedAriaLabel = i18n('selectedAriaLabel', restProps.selectedAriaLabel); const recoveryText = i18n('recoveryText', restProps.recoveryText); if (restProps.recoveryText && !onLoadItems) { warnOnce('Select', '`onLoadItems` must be provided for `recoveryText` to be displayed.'); } const { handleLoadMore, handleRecoveryClick, fireLoadItems } = useLoadItems({ onLoadItems, options, statusType, }); checkControlled('Select', 'selectedOption', selectedOption, 'onChange', onChange); checkOptionValueField('Select', 'options', options); const [filteringValue, setFilteringValue] = useState(''); const { filteredOptions, parentMap, totalCount, matchesCount } = prepareOptions(options, filteringType, filteringValue); const rootRef = useRef(null); const triggerRef = useRef(null); const selfControlId = useUniqueId('trigger'); const controlId = (_b = formFieldContext.controlId) !== null && _b !== void 0 ? _b : selfControlId; const scrollToIndex = useRef(null); const { isOpen, highlightType, highlightedOption, highlightedIndex, getTriggerProps, getDropdownProps, getFilterProps, getMenuProps, getOptionProps, highlightOption, selectOption, announceSelected, } = useSelect({ selectedOptions: selectedOption ? [selectedOption] : [], updateSelectedOption: option => fireNonCancelableEvent(onChange, { selectedOption: option }), options: filteredOptions, filteringType, onBlur, onFocus, externalRef, fireLoadItems, setFilteringValue, statusType, }); const handleNativeSearch = useNativeSearch({ isEnabled: filteringType === 'none', options: filteredOptions, highlightOption: !isOpen ? selectOption : highlightOption, highlightedOption: !isOpen ? selectedOption : highlightedOption === null || highlightedOption === void 0 ? void 0 : highlightedOption.option, }); const selectAriaLabelId = useUniqueId('select-arialabel-'); const footerId = useUniqueId('footer'); useEffect(() => { var _a; (_a = scrollToIndex.current) === null || _a === void 0 ? void 0 : _a.call(scrollToIndex, highlightedIndex); }, [highlightedIndex]); const filter = (React.createElement(Filter, Object.assign({ clearAriaLabel: filteringClearAriaLabel, filteringType: filteringType, placeholder: filteringPlaceholder, ariaLabel: filteringAriaLabel, ariaRequired: ariaRequired, value: filteringValue }, getFilterProps()))); const trigger = (React.createElement(Trigger, Object.assign({ ref: triggerRef, placeholder: placeholder, disabled: disabled, readOnly: readOnly, triggerVariant: triggerVariant, triggerProps: getTriggerProps(disabled, autoFocus), selectedOption: selectedOption, isOpen: isOpen, inFilteringToken: __inFilteringToken, inlineLabelText: inlineLabelText }, formFieldContext, { controlId: controlId, ariaLabelledby: joinStrings(formFieldContext.ariaLabelledby, selectAriaLabelId) }))); const isEmpty = !options || options.length === 0; const isNoMatch = filteredOptions && filteredOptions.length === 0; const isFiltered = filteringType !== 'none' && filteringValue.length > 0 && filteredOptions && filteredOptions.length > 0; const filteredText = isFiltered ? filteringResultsText === null || filteringResultsText === void 0 ? void 0 : filteringResultsText(matchesCount, totalCount) : undefined; const dropdownStatus = useDropdownStatus({ statusType, empty, loadingText, finishedText, errorText, recoveryText, isEmpty, isNoMatch, noMatch, isFiltered, filteringResultsText: filteredText, errorIconAriaLabel, onRecoveryClick: handleRecoveryClick, hasRecoveryCallback: !!onLoadItems, }); const menuProps = Object.assign(Object.assign({}, getMenuProps()), { onLoadMore: handleLoadMore, ariaLabelledby: joinStrings(selectAriaLabelId, controlId), ariaDescribedby: dropdownStatus.content ? footerId : undefined }); const announcement = useAnnouncement({ announceSelected, highlightedOption, getParent: option => { var _a; return (_a = parentMap.get(option)) === null || _a === void 0 ? void 0 : _a.option; }, selectedAriaLabel, renderHighlightedAriaLive, }); const ListComponent = virtualScroll ? VirtualList : PlainList; const handleMouseDown = (event) => { const target = event.target; if (target !== document.activeElement) { // prevent currently focused element from losing it event.preventDefault(); } }; const mergedRef = useMergeRefs(rootRef, __internalRootRef); const dropdownProps = getDropdownProps(); return (React.createElement("div", Object.assign({}, baseProps, { ref: mergedRef, className: clsx(styles.root, baseProps.className), onKeyDown: handleNativeSearch }), React.createElement(Dropdown, Object.assign({}, dropdownProps, { ariaLabelledby: dropdownProps.dropdownContentRole ? joinStrings(selectAriaLabelId, controlId) : undefined, ariaDescribedby: dropdownProps.dropdownContentRole ? (dropdownStatus.content ? footerId : undefined) : undefined, open: isOpen, stretchTriggerHeight: !!__inFilteringToken, stretchBeyondTriggerWidth: true, trigger: trigger, header: filter, onMouseDown: handleMouseDown, footer: dropdownStatus.isSticky ? (React.createElement(DropdownFooter, { content: isOpen ? dropdownStatus.content : null, id: footerId })) : null, expandToViewport: expandToViewport }), React.createElement(ListComponent, { listBottom: !dropdownStatus.isSticky ? (React.createElement(DropdownFooter, { content: isOpen ? dropdownStatus.content : null, id: footerId })) : null, menuProps: menuProps, getOptionProps: getOptionProps, filteredOptions: filteredOptions, filteringValue: filteringValue, ref: scrollToIndex, hasDropdownStatus: dropdownStatus.content !== null, screenReaderContent: announcement, highlightType: highlightType })), React.createElement("div", { hidden: true, id: selectAriaLabelId }, ariaLabel || inlineLabelText))); }); export default InternalSelect; //# sourceMappingURL=internal.js.map