@react-aria/listbox
Version:
Spectrum UI components in React
187 lines (172 loc) • 5.89 kB
JavaScript
import { isFocusVisible, useHover, usePress } from "@react-aria/interactions";
import { useSelectableList, useSelectableItem } from "@react-aria/selection";
import { useLabel } from "@react-aria/label";
import { filterDOMProps, mergeProps, useId, useSlotId } from "@react-aria/utils";
import _babelRuntimeHelpersEsmExtends from "@babel/runtime/helpers/esm/extends";
/*
* Copyright 2020 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
export const listIds = new WeakMap();
function $d7356e622fdc32e5aea4e1e7cc2e6e6b$var$normalizeKey(key) {
if (typeof key === 'string') {
return key.replace(/\s*/g, '');
}
return '' + key;
}
export function getItemId(state, itemKey) {
let listId = listIds.get(state);
if (!listId) {
throw new Error('Unknown list');
}
return listId + "-option-" + $d7356e622fdc32e5aea4e1e7cc2e6e6b$var$normalizeKey(itemKey);
}
/**
* Provides the behavior and accessibility implementation for a listbox component.
* A listbox displays a list of options and allows a user to select one or more of them.
* @param props - Props for the listbox.
* @param state - State for the listbox, as returned by `useListState`.
*/
export function useListBox(props, state, ref) {
let domProps = filterDOMProps(props, {
labelable: true
});
let {
listProps
} = useSelectableList(_babelRuntimeHelpersEsmExtends({}, props, {
ref,
selectionManager: state.selectionManager,
collection: state.collection,
disabledKeys: state.disabledKeys
}));
let id = useId(props.id);
listIds.set(state, id);
let {
labelProps,
fieldProps
} = useLabel(_babelRuntimeHelpersEsmExtends({}, props, {
id,
// listbox is not an HTML input element so it
// shouldn't be labeled by a <label> element.
labelElementType: 'span'
}));
return {
labelProps,
listBoxProps: mergeProps(domProps, state.selectionManager.selectionMode === 'multiple' ? {
'aria-multiselectable': 'true'
} : {}, _babelRuntimeHelpersEsmExtends({
role: 'listbox'
}, mergeProps(fieldProps, listProps)))
};
}
const $d79719e4e3369b10c98de169e0d7fe$var$isSafariMacOS = typeof window !== 'undefined' && window.navigator != null ? /^Mac/.test(window.navigator.platform) && /Safari/.test(window.navigator.userAgent) && !/Chrome/.test(window.navigator.userAgent) : false;
/**
* Provides the behavior and accessibility implementation for an option in a listbox.
* See `useListBox` for more details about listboxes.
* @param props - Props for the option.
* @param state - State for the listbox, as returned by `useListState`.
*/
export function useOption(props, state, ref) {
let {
isSelected,
isDisabled,
key,
shouldSelectOnPressUp,
shouldFocusOnHover,
isVirtualized,
shouldUseVirtualFocus
} = props;
let labelId = useSlotId();
let descriptionId = useSlotId();
let optionProps = {
role: 'option',
'aria-disabled': isDisabled,
'aria-selected': isSelected
}; // Safari with VoiceOver on macOS misreads options with aria-labelledby or aria-label as simply "text".
// We should not map slots to the label and description on Safari and instead just have VoiceOver read the textContent.
// https://bugs.webkit.org/show_bug.cgi?id=209279
if (!$d79719e4e3369b10c98de169e0d7fe$var$isSafariMacOS) {
optionProps['aria-label'] = props['aria-label'];
optionProps['aria-labelledby'] = labelId;
optionProps['aria-describedby'] = descriptionId;
}
if (isVirtualized) {
optionProps['aria-posinset'] = state.collection.getItem(key).index + 1;
optionProps['aria-setsize'] = state.collection.size;
}
let {
itemProps
} = useSelectableItem({
selectionManager: state.selectionManager,
key,
ref,
shouldSelectOnPressUp,
isVirtualized,
shouldUseVirtualFocus
});
let {
pressProps
} = usePress(_babelRuntimeHelpersEsmExtends({}, itemProps, {
isDisabled,
preventFocusOnPress: shouldUseVirtualFocus
}));
let {
hoverProps
} = useHover({
isDisabled: isDisabled || !shouldFocusOnHover,
onHoverStart() {
if (!isFocusVisible()) {
state.selectionManager.setFocused(true);
state.selectionManager.setFocusedKey(key);
}
}
});
return {
optionProps: _babelRuntimeHelpersEsmExtends({}, optionProps, mergeProps(pressProps, hoverProps), {
id: getItemId(state, key)
}),
labelProps: {
id: labelId
},
descriptionProps: {
id: descriptionId
}
};
}
/**
* Provides the behavior and accessibility implementation for a section in a listbox.
* See `useListBox` for more details about listboxes.
* @param props - Props for the section.
*/
export function useListBoxSection(props) {
let {
heading,
'aria-label': ariaLabel
} = props;
let headingId = useId();
return {
itemProps: {
role: 'presentation'
},
headingProps: heading ? {
// Techincally, listbox cannot contain headings according to ARIA.
// We hide the heading from assistive technology, and only use it
// as a label for the nested group.
id: headingId,
'aria-hidden': true
} : {},
groupProps: {
role: 'group',
'aria-label': ariaLabel,
'aria-labelledby': heading ? headingId : undefined
}
};
}
//# sourceMappingURL=module.js.map