Page MenuHomec4science

useSelect.js
No OneTemporary

File Metadata

Created
Sat, Jan 11, 07:18

useSelect.js

import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { unstable_useControlled as useControlled, unstable_useForkRef as useForkRef, unstable_useId as useId } from '@mui/utils';
import { useButton } from '../ButtonUnstyled';
import { useListbox, defaultListboxReducer, ActionTypes } from '../ListboxUnstyled';
import defaultOptionStringifier from './defaultOptionStringifier';
function useSelect(props) {
const {
buttonRef: buttonRefProp,
defaultValue,
disabled = false,
listboxId: listboxIdProp,
listboxRef: listboxRefProp,
multiple = false,
onChange,
onOpenChange,
open = false,
options,
optionStringifier = defaultOptionStringifier,
value: valueProp
} = props;
const buttonRef = React.useRef(null);
const handleButtonRef = useForkRef(buttonRefProp, buttonRef);
const listboxRef = React.useRef(null);
const listboxId = useId(listboxIdProp);
const [value, setValue] = useControlled({
controlled: valueProp,
default: defaultValue,
name: 'SelectUnstyled',
state: 'value'
});
// prevents closing the listbox on keyUp right after opening it
const ignoreEnterKeyUp = React.useRef(false);
// prevents reopening the listbox when button is clicked
// (listbox closes on lost focus, then immediately reopens on click)
const ignoreClick = React.useRef(false);
// Ensure the listbox is focused after opening
const [listboxFocusRequested, requestListboxFocus] = React.useState(false);
const focusListboxIfRequested = React.useCallback(() => {
if (listboxFocusRequested && listboxRef.current != null) {
listboxRef.current.focus();
requestListboxFocus(false);
}
}, [listboxFocusRequested]);
const handleListboxRef = useForkRef(listboxRefProp, listboxRef, focusListboxIfRequested);
React.useEffect(() => {
focusListboxIfRequested();
}, [focusListboxIfRequested]);
React.useEffect(() => {
requestListboxFocus(open);
}, [open]);
const createHandleMouseDown = otherHandlers => event => {
otherHandlers?.onMouseDown?.(event);
if (!event.defaultPrevented && open) {
ignoreClick.current = true;
}
};
const createHandleButtonClick = otherHandlers => event => {
otherHandlers?.onClick?.(event);
if (!event.defaultPrevented && !ignoreClick.current) {
onOpenChange?.(!open);
}
ignoreClick.current = false;
};
const createHandleButtonKeyDown = otherHandlers => event => {
otherHandlers?.onKeyDown?.(event);
if (event.defaultPrevented) {
return;
}
if (event.key === 'Enter') {
ignoreEnterKeyUp.current = true;
}
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
event.preventDefault();
onOpenChange?.(true);
}
};
const createHandleListboxKeyUp = otherHandlers => event => {
otherHandlers?.onKeyUp?.(event);
if (event.defaultPrevented) {
return;
}
const closingKeys = multiple ? ['Escape'] : ['Escape', 'Enter', ' '];
if (open && !ignoreEnterKeyUp.current && closingKeys.includes(event.key)) {
buttonRef?.current?.focus();
}
ignoreEnterKeyUp.current = false;
};
const createHandleListboxItemClick = otherHandlers => event => {
otherHandlers?.onClick?.(event);
if (event.defaultPrevented) {
return;
}
if (!multiple) {
onOpenChange?.(false);
}
};
const createHandleListboxBlur = otherHandlers => event => {
otherHandlers?.onBlur?.(event);
if (!event.defaultPrevented) {
onOpenChange?.(false);
}
};
const listboxReducer = (state, action) => {
const newState = defaultListboxReducer(state, action);
// change selection when listbox is closed
if (action.type === ActionTypes.keyDown && !open && (action.event.key === 'ArrowUp' || action.event.key === 'ArrowDown')) {
return _extends({}, newState, {
selectedValue: newState.highlightedValue
});
}
if (action.type === ActionTypes.blur || action.type === ActionTypes.setValue || action.type === ActionTypes.optionsChange) {
return _extends({}, newState, {
highlightedValue: newState.selectedValue
});
}
return newState;
};
const {
getRootProps: getButtonRootProps,
active: buttonActive,
focusVisible: buttonFocusVisible
} = useButton({
disabled,
ref: handleButtonRef
});
const selectedOption = React.useMemo(() => props.multiple ? props.options.filter(o => value.includes(o.value)) : props.options.find(o => o.value === value) ?? null, [props.multiple, props.options, value]);
let useListboxParameters;
if (props.multiple) {
const onChangeMultiple = onChange;
useListboxParameters = {
id: listboxId,
isOptionDisabled: o => o?.disabled ?? false,
optionComparer: (o, v) => o?.value === v?.value,
listboxRef: handleListboxRef,
multiple: true,
onChange: (e, newOptions) => {
const newValues = newOptions.map(o => o.value);
setValue(newValues);
onChangeMultiple?.(e, newValues);
},
options,
optionStringifier,
value: selectedOption
};
} else {
const onChangeSingle = onChange;
useListboxParameters = {
id: listboxId,
isOptionDisabled: o => o?.disabled ?? false,
optionComparer: (o, v) => o?.value === v?.value,
listboxRef: handleListboxRef,
multiple: false,
onChange: (e, option) => {
setValue(option?.value ?? null);
onChangeSingle?.(e, option?.value ?? null);
},
options,
optionStringifier,
stateReducer: listboxReducer,
value: selectedOption
};
}
const {
getRootProps: getListboxRootProps,
getOptionProps: getListboxOptionProps,
getOptionState,
highlightedOption,
selectedOption: listboxSelectedOption
} = useListbox(useListboxParameters);
const getButtonProps = (otherHandlers = {}) => {
return _extends({}, getButtonRootProps(_extends({}, otherHandlers, {
onClick: createHandleButtonClick(otherHandlers),
onMouseDown: createHandleMouseDown(otherHandlers),
onKeyDown: createHandleButtonKeyDown(otherHandlers)
})), {
role: 'combobox',
'aria-expanded': open,
'aria-haspopup': 'listbox',
'aria-controls': listboxId
});
};
const getListboxProps = (otherHandlers = {}) => getListboxRootProps(_extends({}, otherHandlers, {
onBlur: createHandleListboxBlur(otherHandlers),
onKeyUp: createHandleListboxKeyUp(otherHandlers)
}));
const getOptionProps = (option, otherHandlers = {}) => {
return getListboxOptionProps(option, _extends({}, otherHandlers, {
onClick: createHandleListboxItemClick(otherHandlers)
}));
};
React.useDebugValue({
selectedOption: listboxSelectedOption,
highlightedOption,
open
});
return {
buttonActive,
buttonFocusVisible,
disabled,
getButtonProps,
getListboxProps,
getOptionProps,
getOptionState,
open,
value
};
}
export default useSelect;

Event Timeline