Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F104945763
useListbox.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Thu, Mar 13, 14:16
Size
8 KB
Mime Type
text/x-java
Expires
Sat, Mar 15, 14:16 (2 d)
Engine
blob
Format
Raw Data
Handle
24874008
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
useListbox.js
View Options
import _extends from "@babel/runtime/helpers/esm/extends";
import * as React from 'react';
import { unstable_useForkRef as useForkRef, unstable_useId as useId } from '@mui/utils';
import { ActionTypes } from './useListbox.types';
import defaultReducer from './defaultListboxReducer';
import useControllableReducer from './useControllableReducer';
import areArraysEqual from '../utils/areArraysEqual';
const TEXT_NAVIGATION_RESET_TIMEOUT = 500; // milliseconds
const defaultOptionComparer = (optionA, optionB) => optionA === optionB;
const defaultIsOptionDisabled = () => false;
const defaultOptionStringifier = option => typeof option === 'string' ? option : String(option);
export default function useListbox(props) {
var _props$optionIdGenera, _options$highlightedI;
const {
disabledItemsFocusable = false,
disableListWrap = false,
focusManagement = 'activeDescendant',
id: idProp,
isOptionDisabled = defaultIsOptionDisabled,
listboxRef: externalListboxRef,
multiple = false,
optionComparer = defaultOptionComparer,
optionStringifier = defaultOptionStringifier,
options,
stateReducer: externalReducer
} = props;
const id = useId(idProp);
function defaultIdGenerator(_, index) {
return `${id}-option-${index}`;
}
const optionIdGenerator = (_props$optionIdGenera = props.optionIdGenerator) != null ? _props$optionIdGenera : defaultIdGenerator;
const propsWithDefaults = _extends({}, props, {
disabledItemsFocusable,
disableListWrap,
focusManagement,
isOptionDisabled,
multiple,
optionComparer,
optionStringifier
});
const listboxRef = React.useRef(null);
const handleRef = useForkRef(externalListboxRef, listboxRef);
const textCriteriaRef = React.useRef({
searchString: '',
lastTime: null
});
const [{
highlightedValue,
selectedValue
}, dispatch] = useControllableReducer(defaultReducer, externalReducer, propsWithDefaults);
const highlightedIndex = React.useMemo(() => {
return highlightedValue == null ? -1 : options.findIndex(option => optionComparer(option, highlightedValue));
}, [highlightedValue, options, optionComparer]);
const previousOptions = React.useRef([]);
React.useEffect(() => {
if (areArraysEqual(previousOptions.current, options, optionComparer)) {
return;
}
dispatch({
type: ActionTypes.optionsChange,
event: null,
options,
previousOptions: previousOptions.current,
props: propsWithDefaults
});
previousOptions.current = options;
// No need to re-run this effect if props change
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [options, optionComparer, dispatch]);
const setSelectedValue = React.useCallback(option => {
dispatch({
type: ActionTypes.setValue,
event: null,
value: option
});
}, [dispatch]);
const setHighlightedValue = React.useCallback(option => {
dispatch({
type: ActionTypes.setHighlight,
event: null,
highlight: option
});
}, [dispatch]);
const createHandleOptionClick = (option, other) => event => {
var _other$onClick;
(_other$onClick = other.onClick) == null ? void 0 : _other$onClick.call(other, event);
if (event.defaultPrevented) {
return;
}
event.preventDefault();
dispatch({
type: ActionTypes.optionClick,
option,
event,
props: propsWithDefaults
});
};
const createHandleOptionPointerOver = (option, other) => event => {
var _other$onMouseOver;
(_other$onMouseOver = other.onMouseOver) == null ? void 0 : _other$onMouseOver.call(other, event);
if (event.defaultPrevented) {
return;
}
dispatch({
type: ActionTypes.optionHover,
option,
event,
props: propsWithDefaults
});
};
const createHandleKeyDown = other => event => {
var _other$onKeyDown;
(_other$onKeyDown = other.onKeyDown) == null ? void 0 : _other$onKeyDown.call(other, event);
if (event.defaultPrevented) {
return;
}
const keysToPreventDefault = ['ArrowUp', 'ArrowDown', 'Home', 'End', 'PageUp', 'PageDown'];
if (focusManagement === 'activeDescendant') {
// When the child element is focused using the activeDescendant attribute,
// the listbox handles keyboard events on its behalf.
// We have to `preventDefault()` is this case to prevent the browser from
// scrolling the view when space is pressed or submitting forms when enter is pressed.
keysToPreventDefault.push(' ', 'Enter');
}
if (keysToPreventDefault.includes(event.key)) {
event.preventDefault();
}
dispatch({
type: ActionTypes.keyDown,
event,
props: propsWithDefaults
});
// Handle text navigation
if (event.key.length === 1 && event.key !== ' ') {
const textCriteria = textCriteriaRef.current;
const lowerKey = event.key.toLowerCase();
const currentTime = performance.now();
if (textCriteria.searchString.length > 0 && textCriteria.lastTime && currentTime - textCriteria.lastTime > TEXT_NAVIGATION_RESET_TIMEOUT) {
textCriteria.searchString = lowerKey;
} else if (textCriteria.searchString.length !== 1 || lowerKey !== textCriteria.searchString) {
// If there is just one character in the buffer and the key is the same, do not append
textCriteria.searchString += lowerKey;
}
textCriteria.lastTime = currentTime;
dispatch({
type: ActionTypes.textNavigation,
event,
searchString: textCriteria.searchString,
props: propsWithDefaults
});
}
};
const createHandleBlur = other => event => {
var _other$onBlur, _listboxRef$current;
(_other$onBlur = other.onBlur) == null ? void 0 : _other$onBlur.call(other, event);
if (event.defaultPrevented) {
return;
}
if ((_listboxRef$current = listboxRef.current) != null && _listboxRef$current.contains(document.activeElement)) {
// focus is within the listbox
return;
}
dispatch({
type: ActionTypes.blur,
event,
props: propsWithDefaults
});
};
const getRootProps = (otherHandlers = {}) => {
return _extends({}, otherHandlers, {
'aria-activedescendant': focusManagement === 'activeDescendant' && highlightedValue != null ? optionIdGenerator(highlightedValue, highlightedIndex) : undefined,
id,
onBlur: createHandleBlur(otherHandlers),
onKeyDown: createHandleKeyDown(otherHandlers),
role: 'listbox',
tabIndex: focusManagement === 'DOM' ? -1 : 0,
ref: handleRef
});
};
const getOptionState = option => {
let selected;
const index = options.findIndex(opt => optionComparer(opt, option));
if (multiple) {
var _ref;
selected = ((_ref = selectedValue) != null ? _ref : []).some(value => value != null && optionComparer(option, value));
} else {
selected = optionComparer(option, selectedValue);
}
const disabled = isOptionDisabled(option, index);
return {
selected,
disabled,
highlighted: highlightedIndex === index
};
};
const getOptionTabIndex = optionState => {
if (focusManagement === 'activeDescendant') {
return undefined;
}
if (!optionState.highlighted) {
return -1;
}
if (optionState.disabled && !disabledItemsFocusable) {
return -1;
}
return 0;
};
const getOptionProps = (option, otherHandlers = {}) => {
const optionState = getOptionState(option);
const index = options.findIndex(opt => optionComparer(opt, option));
return _extends({}, otherHandlers, {
'aria-disabled': optionState.disabled || undefined,
'aria-selected': optionState.selected,
id: optionIdGenerator(option, index),
onClick: createHandleOptionClick(option, otherHandlers),
onPointerOver: createHandleOptionPointerOver(option, otherHandlers),
role: 'option',
tabIndex: getOptionTabIndex(optionState)
});
};
React.useDebugValue({
highlightedOption: options[highlightedIndex],
selectedOption: selectedValue
});
return {
getRootProps,
getOptionProps,
getOptionState,
highlightedOption: (_options$highlightedI = options[highlightedIndex]) != null ? _options$highlightedI : null,
selectedOption: selectedValue,
setSelectedValue,
setHighlightedValue
};
}
Event Timeline
Log In to Comment