UNPKG

@mui/joy

Version:

Joy UI is an open-source React component library that implements MUI's own design principles. It's comprehensive and can be used in production out of the box.

1,121 lines (1,117 loc) 40.5 kB
'use client'; import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose"; import _extends from "@babel/runtime/helpers/esm/extends"; var _ClearIcon, _ArrowDropDownIcon; const _excluded = ["aria-describedby", "aria-label", "aria-labelledby", "autoComplete", "autoHighlight", "autoSelect", "autoFocus", "blurOnSelect", "clearIcon", "clearOnBlur", "clearOnEscape", "clearText", "closeText", "defaultValue", "disableCloseOnSelect", "disabledItemsFocusable", "disableListWrap", "disableClearable", "disabled", "endDecorator", "error", "filterOptions", "filterSelectedOptions", "forcePopupIcon", "freeSolo", "getLimitTagsText", "getOptionDisabled", "getOptionKey", "getOptionLabel", "handleHomeEndKeys", "includeInputInList", "isOptionEqualToValue", "groupBy", "id", "inputValue", "limitTags", "loading", "loadingText", "multiple", "name", "noOptionsText", "onChange", "onClose", "onHighlightChange", "onInputChange", "onOpen", "open", "openOnFocus", "openText", "options", "placeholder", "popupIcon", "readOnly", "renderGroup", "renderOption", "renderTags", "required", "type", "startDecorator", "size", "color", "variant", "value", "component", "selectOnFocus", "slots", "slotProps"], _excluded2 = ["onDelete"], _excluded3 = ["key"], _excluded4 = ["onBlur", "onFocus", "onMouseDown"], _excluded5 = ["key"]; import * as React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import { chainPropTypes, integerPropType, unstable_useForkRef as useForkRef, unstable_capitalize as capitalize } from '@mui/utils'; import { unstable_composeClasses as composeClasses } from '@mui/base/composeClasses'; import { useAutocomplete } from '@mui/base/useAutocomplete'; import { Popper } from '@mui/base/Popper'; import { useThemeProps } from '../styles'; import ClearIcon from '../internal/svg-icons/Close'; import ArrowDropDownIcon from '../internal/svg-icons/ArrowDropDown'; import styled from '../styles/styled'; import { VariantColorProvider, getChildVariantAndColor } from '../styles/variantColorInheritance'; // slot components import { StyledIconButton } from '../IconButton/IconButton'; // default render components import Chip from '../Chip'; import ChipDelete from '../ChipDelete'; import { StyledInputRoot, StyledInputHtml, StyledInputStartDecorator, StyledInputEndDecorator } from '../Input/Input'; import List from '../List'; import ListProvider from '../List/ListProvider'; import ListSubheader from '../ListSubheader'; import ListItem from '../ListItem'; import autocompleteClasses, { getAutocompleteUtilityClass } from './autocompleteClasses'; import FormControlContext from '../FormControl/FormControlContext'; import { StyledAutocompleteListbox } from '../AutocompleteListbox/AutocompleteListbox'; import { StyledAutocompleteOption } from '../AutocompleteOption/AutocompleteOption'; import useSlot from '../utils/useSlot'; import { jsx as _jsx } from "react/jsx-runtime"; import { jsxs as _jsxs } from "react/jsx-runtime"; const defaultIsActiveElementInListbox = listboxRef => listboxRef.current !== null && listboxRef.current.contains(document.activeElement); // @ts-ignore const defaultGetOptionLabel = option => { var _option$label; return (_option$label = option.label) != null ? _option$label : option; }; const defaultLimitTagsText = more => `+${more}`; const defaultRenderGroup = params => /*#__PURE__*/_jsxs(ListItem, { nested: true, children: [/*#__PURE__*/_jsx(ListSubheader, { sticky: true, children: params.group }), /*#__PURE__*/_jsx(List, { children: params.children })] }, params.key); const useUtilityClasses = ownerState => { const { disabled, focused, hasClearIcon, hasPopupIcon, popupOpen, variant, color, size, multiple } = ownerState; const slots = { root: ['root', focused && 'focused', hasClearIcon && 'hasClearIcon', hasPopupIcon && 'hasPopupIcon', variant && `variant${capitalize(variant)}`, color && `color${capitalize(color)}`, size && `size${capitalize(size)}`], wrapper: ['wrapper', multiple && 'multiple'], input: ['input'], startDecorator: ['startDecorator'], endDecorator: ['endDecorator'], clearIndicator: ['clearIndicator'], popupIndicator: ['popupIndicator', popupOpen && 'popupIndicatorOpen', disabled && 'disabled'], listbox: ['listbox'], option: ['option'], loading: ['loading'], noOptions: ['noOptions'], limitTag: ['limitTag'] }; return composeClasses(slots, getAutocompleteUtilityClass, {}); }; const AutocompleteRoot = styled(StyledInputRoot, { name: 'JoyAutocomplete', slot: 'Root', overridesResolver: (props, styles) => styles.root })(({ ownerState }) => _extends({}, ownerState.size === 'sm' && { '--Autocomplete-wrapperGap': '3px' }, ownerState.size === 'md' && { '--Autocomplete-wrapperGap': '3px' }, ownerState.size === 'lg' && { '--Autocomplete-wrapperGap': '4px' }, { /* Avoid double tap issue on iOS */ '@media (pointer: fine)': { [`&:hover .${autocompleteClasses.clearIndicator}`]: { visibility: 'visible' } } }, ownerState.multiple && !ownerState.startDecorator && { paddingInlineStart: 0 })); /** * Wrapper groups the chips (multi selection) and the input * so that start/end decorators can stay in the normal flow. */ const AutocompleteWrapper = styled('div', { name: 'JoyAutocomplete', slot: 'Wrapper', overridesResolver: (props, styles) => styles.wrapper })(({ ownerState }) => ({ flex: 1, // stretch to fill the root slot minWidth: 0, // won't push end decorator out of the autocomplete display: 'flex', alignItems: 'center', flexWrap: 'wrap', gap: 'var(--Autocomplete-wrapperGap)', [`&.${autocompleteClasses.multiple}`]: _extends({ paddingBlock: 'var(--Autocomplete-wrapperGap)' }, !ownerState.startDecorator && { paddingInlineStart: 'var(--Autocomplete-wrapperGap)' }, !ownerState.endDecorator && { paddingInlineEnd: 'var(--Autocomplete-wrapperGap)' }) })); const AutocompleteInput = styled(StyledInputHtml, { name: 'JoyAutocomplete', slot: 'Input', overridesResolver: (props, styles) => styles.input })(({ ownerState }) => _extends({ minWidth: 30, minHeight: 'var(--Chip-minHeight)' }, ownerState.multiple && { marginInlineStart: 'calc(var(--Autocomplete-wrapperGap) * 2.5)' })); const AutocompleteStartDecorator = styled(StyledInputStartDecorator, { name: 'JoyAutocomplete', slot: 'StartDecorator', overridesResolver: (props, styles) => styles.startDecorator })({}); const AutocompleteEndDecorator = styled(StyledInputEndDecorator, { name: 'JoyAutocomplete', slot: 'EndDecorator', overridesResolver: (props, styles) => styles.endDecorator })(({ ownerState }) => _extends({}, (ownerState.hasClearIcon || ownerState.hasPopupIcon) && { '--Button-margin': '0px', '--IconButton-margin': '0px', '--Icon-margin': '0px' })); const AutocompleteClearIndicator = styled(StyledIconButton, { name: 'JoyAutocomplete', slot: 'ClearIndicator', overridesResolver: (props, styles) => styles.clearIndicator })(({ ownerState }) => _extends({ alignSelf: 'center' }, !ownerState.hasPopupIcon && { marginInlineEnd: 'calc(var(--Input-decoratorChildOffset) * -1)' }, { marginInlineStart: 'calc(var(--_Input-paddingBlock) / 2)', visibility: ownerState.focused ? 'visible' : 'hidden' })); const AutocompletePopupIndicator = styled(StyledIconButton, { name: 'JoyAutocomplete', slot: 'PopupIndicator', overridesResolver: (props, styles) => styles.popupIndicator })({ alignSelf: 'center', marginInlineStart: 'calc(var(--_Input-paddingBlock) / 2)', marginInlineEnd: 'calc(var(--Input-decoratorChildOffset) * -1)', [`&.${autocompleteClasses.popupIndicatorOpen}`]: { transform: 'rotate(180deg)', '--Icon-color': 'currentColor' } }); const AutocompleteListbox = styled(StyledAutocompleteListbox, { name: 'JoyAutocomplete', slot: 'Listbox', overridesResolver: (props, styles) => styles.listbox })(({ theme }) => ({ // `unstable_popup-zIndex` is a private variable that lets other component, for example Modal, to override the z-index so that the listbox can be displayed above the Modal. zIndex: `var(--unstable_popup-zIndex, ${theme.vars.zIndex.popup})` })); const AutocompleteOption = styled(StyledAutocompleteOption, { name: 'JoyAutocomplete', slot: 'Option', overridesResolver: (props, styles) => styles.option })({}); const AutocompleteLoading = styled(ListItem, { name: 'JoyAutocomplete', slot: 'Loading', overridesResolver: (props, styles) => styles.loading })(({ theme }) => ({ color: (theme.vars || theme).palette.text.secondary })); const AutocompleteNoOptions = styled(ListItem, { name: 'JoyAutocomplete', slot: 'NoOptions', overridesResolver: (props, styles) => styles.noOptions })(({ theme }) => ({ color: (theme.vars || theme).palette.text.secondary })); const AutocompleteLimitTag = styled('div', { name: 'JoyAutocomplete', slot: 'NoOptions', overridesResolver: (props, styles) => styles.noOptions })({ marginInlineStart: 'calc(var(--Input-paddingInline) / 2)', marginBlockStart: 'var(--_Input-paddingBlock)' }); /** * * Demos: * * - [Autocomplete](https://mui.com/joy-ui/react-autocomplete/) * * API: * * - [Autocomplete API](https://mui.com/joy-ui/api/autocomplete/) */ const Autocomplete = /*#__PURE__*/React.forwardRef(function Autocomplete(inProps, ref) { var _ref, _inProps$error, _ref2, _inProps$size, _inProps$color, _formControl$color, _ref3; const props = useThemeProps({ props: inProps, name: 'JoyAutocomplete' }); const { 'aria-describedby': ariaDescribedby, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, autoFocus, clearIcon = _ClearIcon || (_ClearIcon = /*#__PURE__*/_jsx(ClearIcon, { fontSize: "md" })), clearText = 'Clear', closeText = 'Close', disableClearable = false, disabled: disabledProp, endDecorator, error: errorProp = false, forcePopupIcon = 'auto', freeSolo = false, getLimitTagsText = defaultLimitTagsText, getOptionLabel = defaultGetOptionLabel, groupBy, id, limitTags = -1, loading = false, loadingText = 'Loading…', multiple = false, name, noOptionsText = 'No options', openText = 'Open', placeholder, popupIcon = _ArrowDropDownIcon || (_ArrowDropDownIcon = /*#__PURE__*/_jsx(ArrowDropDownIcon, {})), readOnly = false, renderGroup = defaultRenderGroup, renderOption: renderOptionProp, renderTags, required, type, startDecorator, size: sizeProp = 'md', color: colorProp = 'neutral', variant = 'outlined', component, slots = {}, slotProps = {} } = props, other = _objectWithoutPropertiesLoose(props, _excluded); const formControl = React.useContext(FormControlContext); const error = (_ref = (_inProps$error = inProps.error) != null ? _inProps$error : formControl == null ? void 0 : formControl.error) != null ? _ref : errorProp; const size = (_ref2 = (_inProps$size = inProps.size) != null ? _inProps$size : formControl == null ? void 0 : formControl.size) != null ? _ref2 : sizeProp; const color = (_inProps$color = inProps.color) != null ? _inProps$color : error ? 'danger' : (_formControl$color = formControl == null ? void 0 : formControl.color) != null ? _formControl$color : colorProp; const disabled = (_ref3 = disabledProp != null ? disabledProp : formControl == null ? void 0 : formControl.disabled) != null ? _ref3 : false; const { getRootProps, getInputProps, getPopupIndicatorProps, getClearProps, getTagProps, getListboxProps, getOptionProps, value, dirty, popupOpen, focused, focusedTag, anchorEl, setAnchorEl, inputValue, groupedOptions } = useAutocomplete(_extends({}, props, { id: id != null ? id : formControl == null ? void 0 : formControl.htmlFor, componentName: 'Autocomplete', unstable_classNamePrefix: 'Mui', unstable_isActiveElementInListbox: defaultIsActiveElementInListbox })); const { onMouseDown: handleInputMouseDown } = getInputProps(); const { onClick: handleRootOnClick } = getRootProps(); const hasClearIcon = !disableClearable && !disabled && dirty && !readOnly; const hasPopupIcon = (!freeSolo || forcePopupIcon === true) && forcePopupIcon !== false; // If you modify this, make sure to keep the `AutocompleteOwnerState` type in sync. const ownerState = _extends({ instanceColor: inProps.color }, props, { value, disabled, focused, getOptionLabel, hasOptions: !!groupedOptions.length, hasClearIcon, hasPopupIcon, inputFocused: focusedTag === -1, popupOpen, size, color, variant }); const classes = useUtilityClasses(ownerState); const externalForwardedProps = _extends({}, other, { component, slots, slotProps }); let selectedOptions; if (multiple && value.length > 0) { const getCustomizedTagProps = params => { const _getTagProps = getTagProps(params), { onDelete } = _getTagProps, tagProps = _objectWithoutPropertiesLoose(_getTagProps, _excluded2); return _extends({ disabled, size, onClick: onDelete }, tagProps); }; if (renderTags) { selectedOptions = renderTags(value, getCustomizedTagProps, ownerState); } else { selectedOptions = value.map((option, index) => { const _getCustomizedTagProp = getCustomizedTagProps({ index }), { key: endDecoratorKey } = _getCustomizedTagProp, endDecoratorProps = _objectWithoutPropertiesLoose(_getCustomizedTagProp, _excluded3); return /*#__PURE__*/_jsx(Chip, { size: size, variant: "soft", color: "neutral", endDecorator: /*#__PURE__*/_jsx(ChipDelete, _extends({}, endDecoratorProps), endDecoratorKey), sx: { minWidth: 0 }, children: getOptionLabel(option) }, index); }); } } const rootRef = useForkRef(ref, setAnchorEl); const rootStateClasses = { [autocompleteClasses.disabled]: disabled, [autocompleteClasses.error]: error, [autocompleteClasses.focused]: focused, [autocompleteClasses.formControl]: Boolean(formControl) }; const [SlotRoot, rootProps] = useSlot('root', { ref: rootRef, className: [classes.root, rootStateClasses], elementType: AutocompleteRoot, externalForwardedProps, ownerState, getSlotProps: getRootProps, additionalProps: { onClick: event => { if (handleRootOnClick) { handleRootOnClick(event); } if (event.currentTarget === event.target && handleInputMouseDown) { handleInputMouseDown(event); } } } }); const [SlotWrapper, wrapperProps] = useSlot('wrapper', { className: classes.wrapper, elementType: AutocompleteWrapper, externalForwardedProps, ownerState }); const inputStateClasses = { [autocompleteClasses.disabled]: disabled }; const [SlotInput, inputProps] = useSlot('input', { className: [classes.input, inputStateClasses], elementType: AutocompleteInput, getSlotProps: handlers => { const _getInputProps = getInputProps(), { onBlur, onFocus, onMouseDown } = _getInputProps, inputSlotProps = _objectWithoutPropertiesLoose(_getInputProps, _excluded4); return _extends({}, inputSlotProps, { onBlur: event => { var _handlers$onBlur; onBlur == null || onBlur(event); (_handlers$onBlur = handlers.onBlur) == null || _handlers$onBlur.call(handlers, event); }, onFocus: event => { var _handlers$onFocus; onFocus == null || onFocus(event); (_handlers$onFocus = handlers.onFocus) == null || _handlers$onFocus.call(handlers, event); }, onMouseDown: event => { var _handlers$onMouseDown; onMouseDown == null || onMouseDown(event); (_handlers$onMouseDown = handlers.onMouseDown) == null || _handlers$onMouseDown.call(handlers, event); } }); }, externalForwardedProps, ownerState, additionalProps: { autoFocus, placeholder, name, readOnly, disabled, required: required != null ? required : formControl == null ? void 0 : formControl.required, type, 'aria-invalid': error || undefined, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, 'aria-describedby': ariaDescribedby != null ? ariaDescribedby : formControl == null ? void 0 : formControl['aria-describedby'] } }); const [SlotStartDecorator, startDecoratorProps] = useSlot('startDecorator', { className: classes.startDecorator, elementType: AutocompleteStartDecorator, externalForwardedProps, ownerState }); const [SlotEndDecorator, endDecoratorProps] = useSlot('endDecorator', { className: classes.endDecorator, elementType: AutocompleteEndDecorator, externalForwardedProps, ownerState }); const [SlotClearIndicator, clearIndicatorProps] = useSlot('clearIndicator', { className: classes.clearIndicator, elementType: AutocompleteClearIndicator, getSlotProps: getClearProps, externalForwardedProps, ownerState, getSlotOwnerState: mergedProps => ({ size: mergedProps.size || size, variant: mergedProps.variant || getChildVariantAndColor(variant, color).variant || 'plain', color: mergedProps.color || getChildVariantAndColor(variant, color).color || 'neutral', disableColorInversion: !!inProps.color }), additionalProps: { 'aria-label': clearText, title: clearText } }); const [SlotPopupIndicator, popupIndicatorProps] = useSlot('popupIndicator', { className: classes.popupIndicator, elementType: AutocompletePopupIndicator, getSlotProps: getPopupIndicatorProps, externalForwardedProps, ownerState, getSlotOwnerState: mergedProps => ({ size: mergedProps.size || size, variant: mergedProps.variant || getChildVariantAndColor(variant, color).variant || 'plain', color: mergedProps.color || getChildVariantAndColor(variant, color).color || 'neutral', disableColorInversion: !!inProps.color }), additionalProps: { disabled, 'aria-label': popupOpen ? closeText : openText, title: popupOpen ? closeText : openText, type: 'button' } }); const [SlotListbox, listboxProps] = useSlot('listbox', { className: classes.listbox, elementType: AutocompleteListbox, getSlotProps: getListboxProps, externalForwardedProps, ownerState, getSlotOwnerState: mergedProps => ({ size: mergedProps.size || size, variant: mergedProps.variant || variant, color: mergedProps.color || color, disableColorInversion: !mergedProps.disablePortal }), additionalProps: { anchorEl, open: popupOpen, style: anchorEl ? { width: anchorEl.clientWidth } : {} } }); const [SlotLoading, loadingProps] = useSlot('loading', { className: classes.loading, elementType: AutocompleteLoading, externalForwardedProps, ownerState }); const [SlotNoOptions, noOptionsProps] = useSlot('noOptions', { className: classes.noOptions, elementType: AutocompleteNoOptions, externalForwardedProps, ownerState, additionalProps: { role: 'presentation', onMouseDown: event => { // Prevent input blur when interacting with the "no options" content event.preventDefault(); } } }); const [SlotLimitTag, limitTagProps] = useSlot('limitTag', { className: classes.limitTag, elementType: AutocompleteLimitTag, externalForwardedProps, ownerState }); if (limitTags > -1 && Array.isArray(selectedOptions)) { const more = selectedOptions.length - limitTags; if (!focused && more > 0) { selectedOptions = selectedOptions.splice(0, limitTags); selectedOptions.push( /*#__PURE__*/_jsx(SlotLimitTag, _extends({}, limitTagProps, { children: getLimitTagsText(more) }), selectedOptions.length)); } } const [SlotOption, baseOptionProps] = useSlot('option', { className: classes.option, elementType: AutocompleteOption, externalForwardedProps, ownerState, getSlotOwnerState: mergedProps => ({ variant: mergedProps.variant || getChildVariantAndColor(variant, color).variant || 'plain', color: mergedProps.color || getChildVariantAndColor(variant, color).color || 'neutral', disableColorInversion: !listboxProps.disablePortal }), additionalProps: { as: 'li' } }); const defaultRenderOption = (optionProps, option) => { const { key } = optionProps, rest = _objectWithoutPropertiesLoose(optionProps, _excluded5); return /*#__PURE__*/_jsx(SlotOption, _extends({}, rest, { children: getOptionLabel(option) }), key); }; const renderOption = renderOptionProp || defaultRenderOption; const renderListOption = (option, index) => { const optionProps = getOptionProps({ option, index }); return renderOption(_extends({}, baseOptionProps, optionProps), option, { // `aria-selected` prop will always by boolean, see useAutocomplete hook. selected: !!optionProps['aria-selected'], inputValue, ownerState }); }; // Wait for `listboxProps` because `slotProps.listbox` could be a function. const modifiers = React.useMemo(() => [{ name: 'offset', options: { offset: [0, 4] } }, ...(listboxProps.modifiers || [])], [listboxProps.modifiers]); let popup = null; if (anchorEl) { var _props$slots; popup = /*#__PURE__*/_jsx(VariantColorProvider, { variant: variant, color: color, children: /*#__PURE__*/_jsx(ListProvider, { nested: true, children: /*#__PURE__*/_jsxs(SlotListbox, _extends({}, listboxProps, { className: clsx(listboxProps.className) // @ts-ignore internal logic (too complex to typed PopperOwnProps to SlotListbox but this should be removed when we have `usePopper`) , modifiers: modifiers }, !((_props$slots = props.slots) != null && _props$slots.listbox) && { as: Popper, slots: { root: listboxProps.as || 'ul' } }, { children: [groupedOptions.map((option, index) => { if (groupBy) { const typedOption = option; return renderGroup({ key: String(typedOption.key), group: typedOption.group, children: typedOption.options.map((option2, index2) => renderListOption(option2, typedOption.index + index2)) }); } return renderListOption(option, index); }), loading && groupedOptions.length === 0 ? /*#__PURE__*/_jsx(SlotLoading, _extends({}, loadingProps, { children: loadingText })) : null, groupedOptions.length === 0 && !freeSolo && !loading ? /*#__PURE__*/_jsx(SlotNoOptions, _extends({}, noOptionsProps, { children: noOptionsText })) : null] })) }) }); } return /*#__PURE__*/_jsxs(React.Fragment, { children: [/*#__PURE__*/_jsxs(SlotRoot, _extends({}, rootProps, { children: [startDecorator && /*#__PURE__*/_jsx(SlotStartDecorator, _extends({}, startDecoratorProps, { children: startDecorator })), /*#__PURE__*/_jsxs(SlotWrapper, _extends({}, wrapperProps, { children: [selectedOptions, /*#__PURE__*/_jsx(SlotInput, _extends({}, inputProps))] })), endDecorator && /*#__PURE__*/_jsx(SlotEndDecorator, _extends({}, endDecoratorProps, { children: endDecorator })), hasClearIcon ? /*#__PURE__*/_jsx(SlotClearIndicator, _extends({}, clearIndicatorProps, { children: clearIcon })) : null, hasPopupIcon ? /*#__PURE__*/_jsx(SlotPopupIndicator, _extends({}, popupIndicatorProps, { children: popupIcon })) : null] })), popup] }); }); process.env.NODE_ENV !== "production" ? Autocomplete.propTypes /* remove-proptypes */ = { // ┌────────────────────────────── Warning ──────────────────────────────┐ // │ These PropTypes are generated from the TypeScript type definitions. │ // │ To update them, edit the TypeScript types and run `pnpm proptypes`. │ // └─────────────────────────────────────────────────────────────────────┘ /** * Identifies the element (or elements) that describes the object. * @see aria-labelledby */ 'aria-describedby': PropTypes.string, /** * Defines a string value that labels the current element. * @see aria-labelledby. */ 'aria-label': PropTypes.string, /** * Identifies the element (or elements) that labels the current element. * @see aria-describedby. */ 'aria-labelledby': PropTypes.string, /** * If `true`, the portion of the selected suggestion that the user hasn't typed, * known as the completion string, appears inline after the input cursor in the textbox. * The inline completion string is visually highlighted and has a selected state. * @default false */ autoComplete: PropTypes.bool, /** * If `true`, the `input` element is focused during the first mount. */ autoFocus: PropTypes.bool, /** * If `true`, the first option is automatically highlighted. * @default false */ autoHighlight: PropTypes.bool, /** * If `true`, the selected option becomes the value of the input * when the Autocomplete loses focus unless the user chooses * a different option or changes the character string in the input. * * When using the `freeSolo` mode, the typed value will be the input value * if the Autocomplete loses focus without highlighting an option. * @default false */ autoSelect: PropTypes.bool, /** * Control if the input should be blurred when an option is selected: * * - `false` the input is not blurred. * - `true` the input is always blurred. * - `touch` the input is blurred after a touch event. * - `mouse` the input is blurred after a mouse event. * @default false */ blurOnSelect: PropTypes.oneOfType([PropTypes.oneOf(['mouse', 'touch']), PropTypes.bool]), /** * The icon to display in place of the default clear icon. * @default <ClearIcon fontSize="md" /> */ clearIcon: PropTypes.node, /** * If `true`, the input's text is cleared on blur if no value is selected. * * Set it to `true` if you want to help the user enter a new value. * Set it to `false` if you want to help the user resume their search. * @default !props.freeSolo */ clearOnBlur: PropTypes.bool, /** * If `true`, clear all values when the user presses escape and the popup is closed. * @default false */ clearOnEscape: PropTypes.bool, /** * Override the default text for the *clear* icon button. * * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). * @default 'Clear' */ clearText: PropTypes.string, /** * Override the default text for the *close popup* icon button. * * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). * @default 'Close' */ closeText: PropTypes.string, /** * The color of the component. It supports those theme colors that make sense for this component. * @default 'neutral' */ color: PropTypes.oneOf(['danger', 'neutral', 'primary', 'success', 'warning']), /** * The default value. Use when the component is not controlled. * @default props.multiple ? [] : null */ defaultValue: chainPropTypes(PropTypes.any, props => { if (props.multiple && props.defaultValue !== undefined && !Array.isArray(props.defaultValue)) { return new Error(['MUI: The Autocomplete expects the `defaultValue` prop to be an array when `multiple={true}` or undefined.', `However, ${props.defaultValue} was provided.`].join('\n')); } return null; }), /** * If `true`, the input can't be cleared. * @default false */ disableClearable: PropTypes.bool, /** * If `true`, the popup won't close when a value is selected. * @default false */ disableCloseOnSelect: PropTypes.bool, /** * If `true`, the component is disabled. * @default false */ disabled: PropTypes.bool, /** * If `true`, will allow focus on disabled items. * @default false */ disabledItemsFocusable: PropTypes.bool, /** * If `true`, the list box in the popup will not wrap focus. * @default false */ disableListWrap: PropTypes.bool, /** * Trailing adornment for this input. */ endDecorator: PropTypes.node, /** * If `true`, the `input` will indicate an error. * The prop defaults to the value (`false`) inherited from the parent FormControl component. * @default false */ error: PropTypes.bool, /** * A function that determines the filtered options to be rendered on search. * * @default createFilterOptions() * @param {Value[]} options The options to render. * @param {object} state The state of the component. * @returns {Value[]} */ filterOptions: PropTypes.func, /** * If `true`, hide the selected options from the list box. * @default false */ filterSelectedOptions: PropTypes.bool, /** * Force the visibility display of the popup icon. * @default 'auto' */ forcePopupIcon: PropTypes.oneOfType([PropTypes.oneOf(['auto']), PropTypes.bool]), /** * If `true`, the Autocomplete is free solo, meaning that the user input is not bound to provided options. * @default false */ freeSolo: PropTypes.bool, /** * The label to display when the tags are truncated (`limitTags`). * * @param {string | number} more The number of truncated tags. * @returns {ReactNode} * @default (more: string | number) => `+${more}` */ getLimitTagsText: PropTypes.func, /** * Used to determine the disabled state for a given option. * * @param {Value} option The option to test. * @returns {boolean} */ getOptionDisabled: PropTypes.func, /** * Used to determine the key for a given option. * This can be useful when the labels of options are not unique (since labels are used as keys by default). * * @param {Value} option The option to get the key for. * @returns {string | number} */ getOptionKey: PropTypes.func, /** * Used to determine the string value for a given option. * It's used to fill the input (and the list box options if `renderOption` is not provided). * * If used in free solo mode, it must accept both the type of the options and a string. * * @param {Value} option * @returns {string} * @default (option) => option.label ?? option */ getOptionLabel: PropTypes.func, /** * If provided, the options will be grouped under the returned string. * The groupBy value is also used as the text for group headings when `renderGroup` is not provided. * * @param {Value} options The options to group. * @returns {string} */ groupBy: PropTypes.func, /** * If `true`, the component handles the "Home" and "End" keys when the popup is open. * It should move focus to the first option and last option, respectively. * @default !props.freeSolo */ handleHomeEndKeys: PropTypes.bool, /** * This prop is used to help implement the accessibility logic. * If you don't provide an id it will fall back to a randomly generated one. */ id: PropTypes.string, /** * If `true`, the highlight can move to the input. * @default false */ includeInputInList: PropTypes.bool, /** * The input value. */ inputValue: PropTypes.string, /** * Used to determine if the option represents the given value. * Uses strict equality by default. * ⚠️ Both arguments need to be handled, an option can only match with one value. * * @param {Value} option The option to test. * @param {Value} value The value to test against. * @returns {boolean} */ isOptionEqualToValue: PropTypes.func, /** * The maximum number of tags that will be visible when not focused. * Set `-1` to disable the limit. * @default -1 */ limitTags: integerPropType, /** * If `true`, the component is in a loading state. * This shows the `loadingText` in place of suggestions (only if there are no suggestions to show, for example `options` are empty). * @default false */ loading: PropTypes.bool, /** * Text to display when in a loading state. * * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). * @default 'Loading…' */ loadingText: PropTypes.node, /** * If `true`, `value` must be an array and the menu will support multiple selections. * @default false */ multiple: PropTypes.bool, /** * Name attribute of the `input` element. */ name: PropTypes.string, /** * Text to display when there are no options. * * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). * @default 'No options' */ noOptionsText: PropTypes.node, /** * Callback fired when the value changes. * * @param {React.SyntheticEvent} event The event source of the callback. * @param {Value|Value[]} value The new value of the component. * @param {string} reason One of "createOption", "selectOption", "removeOption", "blur" or "clear". * @param {string} [details] */ onChange: PropTypes.func, /** * Callback fired when the popup requests to be closed. * Use in controlled mode (see open). * * @param {React.SyntheticEvent} event The event source of the callback. * @param {string} reason Can be: `"toggleInput"`, `"escape"`, `"selectOption"`, `"removeOption"`, `"blur"`. */ onClose: PropTypes.func, /** * Callback fired when the highlight option changes. * * @param {React.SyntheticEvent} event The event source of the callback. * @param {Value} option The highlighted option. * @param {string} reason Can be: `"keyboard"`, `"auto"`, `"mouse"`, `"touch"`. */ onHighlightChange: PropTypes.func, /** * Callback fired when the input value changes. * * @param {React.SyntheticEvent} event The event source of the callback. * @param {string} value The new value of the text input. * @param {string} reason Can be: `"input"` (user input), `"reset"` (programmatic change), `"clear"`. */ onInputChange: PropTypes.func, /** * @ignore */ onKeyDown: PropTypes.func, /** * Callback fired when the popup requests to be opened. * Use in controlled mode (see open). * * @param {React.SyntheticEvent} event The event source of the callback. */ onOpen: PropTypes.func, /** * If `true`, the component is shown. */ open: PropTypes.bool, /** * If `true`, the popup will open on input focus. * @default false */ openOnFocus: PropTypes.bool, /** * Override the default text for the *open popup* icon button. * * For localization purposes, you can use the provided [translations](/material-ui/guides/localization/). * @default 'Open' */ openText: PropTypes.string, /** * Array of options. */ options: PropTypes.array.isRequired, /** * The input placeholder */ placeholder: PropTypes.string, /** * The icon to display in place of the default popup icon. * @default <ArrowDropDownIcon /> */ popupIcon: PropTypes.node, /** * If `true`, the component becomes readonly. It is also supported for multiple tags where the tag cannot be deleted. * @default false */ readOnly: PropTypes.bool, /** * Render the group. * * @param {AutocompleteRenderGroupParams} params The group to render. * @returns {ReactNode} */ renderGroup: PropTypes.func, /** * Render the option, use `getOptionLabel` by default. * * @param {object} props The props to apply on the li element. * @param {T} option The option to render. * @param {object} state The state of the component. * @returns {ReactNode} */ renderOption: PropTypes.func, /** * Render the selected value. * * @param {T[]} value The `value` provided to the component. * @param {function} getTagProps A tag props getter. * @param {object} ownerState The state of the Autocomplete component. * @returns {ReactNode} */ renderTags: PropTypes.func, /** * If `true`, the `input` element is required. * The prop defaults to the value (`false`) inherited from the parent FormControl component. */ required: PropTypes.bool, /** * If `true`, the input's text is selected on focus. * It helps the user clear the selected value. * @default !props.freeSolo */ selectOnFocus: PropTypes.bool, /** * The size of the component. * @default 'md' */ size: PropTypes /* @typescript-to-proptypes-ignore */.oneOfType([PropTypes.oneOf(['sm', 'md', 'lg']), PropTypes.string]), /** * The props used for each slot inside. * @default {} */ slotProps: PropTypes.shape({ clearIndicator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), endDecorator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), input: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), limitTag: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), listbox: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), loading: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), noOptions: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), option: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), popupIndicator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), root: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), startDecorator: PropTypes.oneOfType([PropTypes.func, PropTypes.object]), wrapper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]) }), /** * The components used for each slot inside. * @default {} */ slots: PropTypes.shape({ clearIndicator: PropTypes.elementType, endDecorator: PropTypes.elementType, input: PropTypes.elementType, limitTag: PropTypes.elementType, listbox: PropTypes.elementType, loading: PropTypes.elementType, noOptions: PropTypes.elementType, option: PropTypes.elementType, popupIndicator: PropTypes.elementType, root: PropTypes.elementType, startDecorator: PropTypes.elementType, wrapper: PropTypes.elementType }), /** * Leading adornment for this input. */ startDecorator: PropTypes.node, /** * The system prop that allows defining system overrides as well as additional CSS styles. */ sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]), /** * Type of the `input` element. It should be [a valid HTML5 input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#Form_%3Cinput%3E_types). */ type: PropTypes.string, /** * The value of the autocomplete. * * The value must have reference equality with the option in order to be selected. * You can customize the equality behavior with the `isOptionEqualToValue` prop. */ value: chainPropTypes(PropTypes.any, props => { if (props.multiple && props.value !== undefined && !Array.isArray(props.value)) { return new Error(['MUI: The Autocomplete expects the `value` prop to be an array when `multiple={true}` or undefined.', `However, ${props.value} was provided.`].join('\n')); } return null; }), /** * The [global variant](https://mui.com/joy-ui/main-features/global-variants/) to use. * @default 'outlined' */ variant: PropTypes.oneOf(['outlined', 'plain', 'soft', 'solid']) } : void 0; export default Autocomplete;