UNPKG

@react-aria/listbox

Version:
187 lines (172 loc) 5.89 kB
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