UNPKG

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