UNPKG

9.54 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3const _excluded = ["autoFocus", "children", "defaultValue", "defaultListboxOpen", "disabled", "getSerializedValue", "listboxId", "listboxOpen", "multiple", "name", "onChange", "onListboxOpenChange", "getOptionAsString", "renderValue", "slotProps", "slots", "value"];
4import * as React from 'react';
5import PropTypes from 'prop-types';
6import { unstable_useForkRef as useForkRef } from '@mui/utils';
7import useSelect from '../useSelect';
8import { useSlotProps } from '../utils';
9import Popper from '../Popper';
10import composeClasses from '../composeClasses';
11import { getSelectUtilityClass } from './selectClasses';
12import defaultOptionStringifier from '../useSelect/defaultOptionStringifier';
13import { useClassNamesOverride } from '../utils/ClassNameConfigurator';
14import SelectProvider from '../useSelect/SelectProvider';
15import { jsx as _jsx } from "react/jsx-runtime";
16import { jsxs as _jsxs } from "react/jsx-runtime";
17function defaultRenderValue(selectedOptions) {
18 if (Array.isArray(selectedOptions)) {
19 return /*#__PURE__*/_jsx(React.Fragment, {
20 children: selectedOptions.map(o => o.label).join(', ')
21 });
22 }
23 return selectedOptions?.label ?? '';
24}
25function defaultFormValueProvider(selectedOption) {
26 if (Array.isArray(selectedOption)) {
27 if (selectedOption.length === 0) {
28 return '';
29 }
30 if (selectedOption.every(o => typeof o.value === 'string' || typeof o.value === 'number' || typeof o.value === 'boolean')) {
31 return selectedOption.map(o => String(o.value));
32 }
33 return JSON.stringify(selectedOption.map(o => o.value));
34 }
35 if (selectedOption?.value == null) {
36 return '';
37 }
38 if (typeof selectedOption.value === 'string' || typeof selectedOption.value === 'number') {
39 return selectedOption.value;
40 }
41 return JSON.stringify(selectedOption.value);
42}
43function useUtilityClasses(ownerState) {
44 const {
45 active,
46 disabled,
47 open,
48 focusVisible
49 } = ownerState;
50 const slots = {
51 root: ['root', disabled && 'disabled', focusVisible && 'focusVisible', active && 'active', open && 'expanded'],
52 listbox: ['listbox', disabled && 'disabled'],
53 popper: ['popper']
54 };
55 return composeClasses(slots, useClassNamesOverride(getSelectUtilityClass));
56}
57
58/**
59 * The foundation for building custom-styled select components.
60 *
61 * Demos:
62 *
63 * - [Select](https://mui.com/base/react-select/)
64 *
65 * API:
66 *
67 * - [Select API](https://mui.com/base/react-select/components-api/#select)
68 */
69const Select = /*#__PURE__*/React.forwardRef(function Select(props, forwardedRef) {
70 const {
71 autoFocus,
72 children,
73 defaultValue,
74 defaultListboxOpen = false,
75 disabled: disabledProp,
76 getSerializedValue = defaultFormValueProvider,
77 listboxId,
78 listboxOpen: listboxOpenProp,
79 multiple = false,
80 name,
81 onChange,
82 onListboxOpenChange,
83 getOptionAsString = defaultOptionStringifier,
84 renderValue: renderValueProp,
85 slotProps = {},
86 slots = {},
87 value: valueProp
88 } = props,
89 other = _objectWithoutPropertiesLoose(props, _excluded);
90 const renderValue = renderValueProp ?? defaultRenderValue;
91 const [buttonDefined, setButtonDefined] = React.useState(false);
92 const buttonRef = React.useRef(null);
93 const listboxRef = React.useRef(null);
94 const Button = slots.root ?? 'button';
95 const ListboxRoot = slots.listbox ?? 'ul';
96 const PopperComponent = slots.popper ?? Popper;
97 const handleButtonRefChange = React.useCallback(element => {
98 setButtonDefined(element != null);
99 }, []);
100 const handleButtonRef = useForkRef(forwardedRef, buttonRef, handleButtonRefChange);
101 React.useEffect(() => {
102 if (autoFocus) {
103 buttonRef.current.focus();
104 }
105 }, [autoFocus]);
106 const {
107 buttonActive,
108 buttonFocusVisible,
109 contextValue,
110 disabled,
111 getButtonProps,
112 getListboxProps,
113 getOptionMetadata,
114 value,
115 open
116 } = useSelect({
117 buttonRef: handleButtonRef,
118 defaultOpen: defaultListboxOpen,
119 defaultValue,
120 disabled: disabledProp,
121 listboxId,
122 multiple,
123 open: listboxOpenProp,
124 onChange,
125 onOpenChange: onListboxOpenChange,
126 getOptionAsString,
127 value: valueProp
128 });
129 const ownerState = _extends({}, props, {
130 active: buttonActive,
131 defaultListboxOpen,
132 disabled,
133 focusVisible: buttonFocusVisible,
134 open,
135 multiple,
136 renderValue,
137 value
138 });
139 const classes = useUtilityClasses(ownerState);
140 const buttonProps = useSlotProps({
141 elementType: Button,
142 getSlotProps: getButtonProps,
143 externalSlotProps: slotProps.root,
144 externalForwardedProps: other,
145 ownerState,
146 className: classes.root
147 });
148 const listboxProps = useSlotProps({
149 elementType: ListboxRoot,
150 getSlotProps: getListboxProps,
151 externalSlotProps: slotProps.listbox,
152 additionalProps: {
153 ref: listboxRef
154 },
155 ownerState,
156 className: classes.listbox
157 });
158 const popperProps = useSlotProps({
159 elementType: PopperComponent,
160 externalSlotProps: slotProps.popper,
161 additionalProps: {
162 anchorEl: buttonRef.current,
163 keepMounted: true,
164 open,
165 placement: 'bottom-start',
166 role: undefined
167 },
168 ownerState,
169 className: classes.popper
170 });
171 let selectedOptionsMetadata;
172 if (multiple) {
173 selectedOptionsMetadata = value.map(v => getOptionMetadata(v)).filter(o => o !== undefined);
174 } else {
175 selectedOptionsMetadata = getOptionMetadata(value) ?? null;
176 }
177 return /*#__PURE__*/_jsxs(React.Fragment, {
178 children: [/*#__PURE__*/_jsx(Button, _extends({}, buttonProps, {
179 children: renderValue(selectedOptionsMetadata)
180 })), buttonDefined && /*#__PURE__*/_jsx(PopperComponent, _extends({}, popperProps, {
181 children: /*#__PURE__*/_jsx(ListboxRoot, _extends({}, listboxProps, {
182 children: /*#__PURE__*/_jsx(SelectProvider, {
183 value: contextValue,
184 children: children
185 })
186 }))
187 })), name && /*#__PURE__*/_jsx("input", {
188 type: "hidden",
189 name: name,
190 value: getSerializedValue(selectedOptionsMetadata)
191 })]
192 });
193});
194process.env.NODE_ENV !== "production" ? Select.propTypes /* remove-proptypes */ = {
195 // ----------------------------- Warning --------------------------------
196 // | These PropTypes are generated from the TypeScript type definitions |
197 // | To update them edit TypeScript types and run "yarn proptypes" |
198 // ----------------------------------------------------------------------
199 /**
200 * If `true`, the select element is focused during the first mount
201 * @default false
202 */
203 autoFocus: PropTypes.bool,
204 /**
205 * @ignore
206 */
207 children: PropTypes.node,
208 /**
209 * If `true`, the select will be initially open.
210 * @default false
211 */
212 defaultListboxOpen: PropTypes.bool,
213 /**
214 * The default selected value. Use when the component is not controlled.
215 */
216 defaultValue: PropTypes.any,
217 /**
218 * If `true`, the select is disabled.
219 * @default false
220 */
221 disabled: PropTypes.bool,
222 /**
223 * A function used to convert the option label to a string.
224 * It's useful when labels are elements and need to be converted to plain text
225 * to enable navigation using character keys on a keyboard.
226 *
227 * @default defaultOptionStringifier
228 */
229 getOptionAsString: PropTypes.func,
230 /**
231 * A function to convert the currently selected value to a string.
232 * Used to set a value of a hidden input associated with the select,
233 * so that the selected value can be posted with a form.
234 */
235 getSerializedValue: PropTypes.func,
236 /**
237 * `id` attribute of the listbox element.
238 */
239 listboxId: PropTypes.string,
240 /**
241 * Controls the open state of the select's listbox.
242 * @default undefined
243 */
244 listboxOpen: PropTypes.bool,
245 /**
246 * If `true`, selecting multiple values is allowed.
247 * This affects the type of the `value`, `defaultValue`, and `onChange` props.
248 *
249 * @default false
250 */
251 multiple: PropTypes.bool,
252 /**
253 * Name of the element. For example used by the server to identify the fields in form submits.
254 * If the name is provided, the component will render a hidden input element that can be submitted to a server.
255 */
256 name: PropTypes.string,
257 /**
258 * Callback fired when an option is selected.
259 */
260 onChange: PropTypes.func,
261 /**
262 * Callback fired when the component requests to be opened.
263 * Use in controlled mode (see listboxOpen).
264 */
265 onListboxOpenChange: PropTypes.func,
266 /**
267 * Function that customizes the rendering of the selected value.
268 */
269 renderValue: PropTypes.func,
270 /**
271 * The props used for each slot inside the Input.
272 * @default {}
273 */
274 slotProps: PropTypes /* @typescript-to-proptypes-ignore */.shape({
275 listbox: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
276 popper: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
277 root: PropTypes.oneOfType([PropTypes.func, PropTypes.object])
278 }),
279 /**
280 * The components used for each slot inside the Select.
281 * Either a string to use a HTML element or a component.
282 * @default {}
283 */
284 slots: PropTypes /* @typescript-to-proptypes-ignore */.shape({
285 listbox: PropTypes.elementType,
286 popper: PropTypes.elementType,
287 root: PropTypes.elementType
288 }),
289 /**
290 * The selected value.
291 * Set to `null` to deselect all options.
292 */
293 value: PropTypes.any
294} : void 0;
295export default Select;
\No newline at end of file