UNPKG

react-aria

Version:
367 lines (349 loc) • 21.1 kB
import {chain as $2cf8bb4b9e45dc81$export$e08e3b67e392101e} from "../utils/chain.js"; import {focusSafely as $56c81cdebdc6a696$export$80f3e147d781571c} from "../interactions/focusSafely.js"; import {getActiveElement as $d8ac7ed472840322$export$cd4e5573fbe2b576, getEventTarget as $d8ac7ed472840322$export$e58f029f0fbfdb29, isFocusWithin as $d8ac7ed472840322$export$b4f377a2b6254582, nodeContains as $d8ac7ed472840322$export$4282f70798064fe0} from "../utils/shadowdom/DOMFunctions.js"; import {getFocusableTreeWalker as $903814aeb7d53b38$export$2d6ec8fc375ceafa} from "../focus/FocusScope.js"; import {getRowId as $781ed2e01df48c52$export$f45c25170b9a99c2, listMap as $781ed2e01df48c52$export$5b9bb410392e3991} from "./utils.js"; import {getScrollParent as $5b46e0a1626c2890$export$cfa2225e87938781} from "../utils/getScrollParent.js"; import {isFocusVisible as $b50b1cc8a843ace7$export$b9b3dfddab17db27} from "../interactions/useFocusVisible.js"; import {isTabbable as $ee5e22534121197a$export$bebd5a1431fec25d} from "../utils/isFocusable.js"; import {mergeProps as $64c36edd757dfa16$export$9d1611c77c2fe928} from "../utils/mergeProps.js"; import {scrollIntoViewport as $6507765bd7f5ad94$export$c826860796309d1b} from "../utils/scrollIntoView.js"; import {useSelectableItem as $0d8cf6a15fe85601$export$ecf600387e221c37} from "../selection/useSelectableItem.js"; import {useLocale as $4defb058003b3e05$export$43bb16f9c6d9e3f7} from "../i18n/I18nProvider.js"; import {useSlotId as $0292efe68908de6b$export$b4cc09c592e8fdb8} from "../utils/useId.js"; import {useSyntheticLinkProps as $044d3c97ce5d6621$export$bdc77b0c0a3a85d6} from "../utils/openLink.js"; import {useRef as $45hvd$useRef} from "react"; /* * Copyright 2022 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. */ const $7d08662b45460e3b$var$EXPANSION_KEYS = { expand: { ltr: 'ArrowRight', rtl: 'ArrowLeft' }, collapse: { ltr: 'ArrowLeft', rtl: 'ArrowRight' } }; function $7d08662b45460e3b$export$9610e69494fadfd2(props, state, ref) { var _node_props, _node_props1; // Copied from useGridCell + some modifications to make it not so grid specific let { node: node, isVirtualized: isVirtualized } = props; // let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-aria/gridlist'); let { direction: direction } = (0, $4defb058003b3e05$export$43bb16f9c6d9e3f7)(); let { onAction: onAction, linkBehavior: linkBehavior, keyboardNavigationBehavior: keyboardNavigationBehavior, shouldSelectOnPressUp: shouldSelectOnPressUp } = (0, $781ed2e01df48c52$export$5b9bb410392e3991).get(state); let descriptionId = (0, $0292efe68908de6b$export$b4cc09c592e8fdb8)(); // We need to track the key of the item at the time it was last focused so that we force // focus to go to the item when the DOM node is reused for a different item in a virtualizer. let keyWhenFocused = (0, $45hvd$useRef)(null); let focus = ()=>{ // Don't shift focus to the row if the active element is a element within the row already // (e.g. clicking on a row button) if (ref.current !== null && (keyWhenFocused.current != null && node.key !== keyWhenFocused.current || !(0, $d8ac7ed472840322$export$b4f377a2b6254582)(ref.current))) (0, $56c81cdebdc6a696$export$80f3e147d781571c)(ref.current); }; let treeGridRowProps = {}; let hasChildRows = props.hasChildItems; let hasLink = state.selectionManager.isLink(node.key); if (node != null && 'expandedKeys' in state) { var _state_collection_getChildren, _state_collection; // TODO: ideally node.hasChildNodes would be a way to tell if a row has child nodes, but the row's contents make it so that value is always // true... let children = (_state_collection_getChildren = (_state_collection = state.collection).getChildren) === null || _state_collection_getChildren === void 0 ? void 0 : _state_collection_getChildren.call(_state_collection, node.key); hasChildRows = hasChildRows || [ ...children !== null && children !== void 0 ? children : [] ].length > 1; if (onAction == null && !hasLink && state.selectionManager.selectionMode === 'none' && hasChildRows) onAction = ()=>state.toggleKey(node.key); let isExpanded = hasChildRows ? state.expandedKeys.has(node.key) : undefined; let setSize = 1; let index = node.index; if (node.level >= 0 && (node === null || node === void 0 ? void 0 : node.parentKey) != null) { let parent = state.collection.getItem(node.parentKey); if (parent) { // siblings must exist because our original node exists let siblings = $7d08662b45460e3b$var$getDirectChildren(parent, state.collection); setSize = [ ...siblings ].filter((row)=>row.type === 'item').length; if (index > 0 && siblings[0].type !== 'item') index -= 1; // subtract one for the parent item's content node } } else setSize = [ ...state.collection ].filter((row)=>row.level === 0 && row.type === 'item').length; treeGridRowProps = { 'aria-expanded': isExpanded, 'aria-level': node.level + 1, 'aria-posinset': index + 1, 'aria-setsize': setSize }; } let { itemProps: itemProps, ...itemStates } = (0, $0d8cf6a15fe85601$export$ecf600387e221c37)({ selectionManager: state.selectionManager, key: node.key, ref: ref, isVirtualized: isVirtualized, shouldSelectOnPressUp: props.shouldSelectOnPressUp || shouldSelectOnPressUp, onAction: onAction || ((_node_props = node.props) === null || _node_props === void 0 ? void 0 : _node_props.onAction) ? (0, $2cf8bb4b9e45dc81$export$e08e3b67e392101e)((_node_props1 = node.props) === null || _node_props1 === void 0 ? void 0 : _node_props1.onAction, onAction ? ()=>onAction(node.key) : undefined) : undefined, focus: focus, linkBehavior: linkBehavior }); let onKeyDownCapture = (e)=>{ let activeElement = (0, $d8ac7ed472840322$export$cd4e5573fbe2b576)(); if (!(0, $d8ac7ed472840322$export$4282f70798064fe0)(e.currentTarget, (0, $d8ac7ed472840322$export$e58f029f0fbfdb29)(e)) || !ref.current || !activeElement) return; let walker = (0, $903814aeb7d53b38$export$2d6ec8fc375ceafa)(ref.current); walker.currentNode = activeElement; if ($7d08662b45460e3b$var$handleTreeExpansionKeys(e, state, node, hasChildRows, direction, activeElement, ref.current)) return; switch(e.key){ case 'ArrowLeft': if (keyboardNavigationBehavior === 'arrow') { // Find the next focusable element within the row. let focusable = direction === 'rtl' ? walker.nextNode() : walker.previousNode(); if (focusable) { e.preventDefault(); e.stopPropagation(); (0, $56c81cdebdc6a696$export$80f3e147d781571c)(focusable); (0, $6507765bd7f5ad94$export$c826860796309d1b)(focusable, { containingElement: (0, $5b46e0a1626c2890$export$cfa2225e87938781)(ref.current) }); } else { // If there is no next focusable child, then return focus back to the row e.preventDefault(); e.stopPropagation(); if (direction === 'rtl') { (0, $56c81cdebdc6a696$export$80f3e147d781571c)(ref.current); (0, $6507765bd7f5ad94$export$c826860796309d1b)(ref.current, { containingElement: (0, $5b46e0a1626c2890$export$cfa2225e87938781)(ref.current) }); } else { walker.currentNode = ref.current; let lastElement = $7d08662b45460e3b$var$last(walker); // oxlint-disable-next-line max-depth if (lastElement) { (0, $56c81cdebdc6a696$export$80f3e147d781571c)(lastElement); (0, $6507765bd7f5ad94$export$c826860796309d1b)(lastElement, { containingElement: (0, $5b46e0a1626c2890$export$cfa2225e87938781)(ref.current) }); } } } } break; case 'ArrowRight': if (keyboardNavigationBehavior === 'arrow') { let focusable = direction === 'rtl' ? walker.previousNode() : walker.nextNode(); if (focusable) { e.preventDefault(); e.stopPropagation(); (0, $56c81cdebdc6a696$export$80f3e147d781571c)(focusable); (0, $6507765bd7f5ad94$export$c826860796309d1b)(focusable, { containingElement: (0, $5b46e0a1626c2890$export$cfa2225e87938781)(ref.current) }); } else { e.preventDefault(); e.stopPropagation(); if (direction === 'ltr') { (0, $56c81cdebdc6a696$export$80f3e147d781571c)(ref.current); (0, $6507765bd7f5ad94$export$c826860796309d1b)(ref.current, { containingElement: (0, $5b46e0a1626c2890$export$cfa2225e87938781)(ref.current) }); } else { walker.currentNode = ref.current; let lastElement = $7d08662b45460e3b$var$last(walker); // oxlint-disable-next-line max-depth if (lastElement) { (0, $56c81cdebdc6a696$export$80f3e147d781571c)(lastElement); (0, $6507765bd7f5ad94$export$c826860796309d1b)(lastElement, { containingElement: (0, $5b46e0a1626c2890$export$cfa2225e87938781)(ref.current) }); } } } } break; case 'ArrowUp': case 'ArrowDown': // Prevent this event from reaching row children, e.g. menu buttons. We want arrow keys to navigate // to the row above/below instead. We need to re-dispatch the event from a higher parent so it still // bubbles and gets handled by useSelectableCollection. if (!e.altKey && (0, $d8ac7ed472840322$export$4282f70798064fe0)(ref.current, (0, $d8ac7ed472840322$export$e58f029f0fbfdb29)(e))) { var _ref_current_parentElement; e.stopPropagation(); e.preventDefault(); (_ref_current_parentElement = ref.current.parentElement) === null || _ref_current_parentElement === void 0 ? void 0 : _ref_current_parentElement.dispatchEvent(new KeyboardEvent(e.nativeEvent.type, e.nativeEvent)); } break; } }; let onFocus = (e)=>{ keyWhenFocused.current = node.key; if ((0, $d8ac7ed472840322$export$e58f029f0fbfdb29)(e) !== ref.current) { // useSelectableItem only handles setting the focused key when // the focused element is the row itself. We also want to // set the focused key when a child element receives focus. // If focus is currently visible (e.g. the user is navigating with the keyboard), // then skip this. We want to restore focus to the previously focused row // in that case since the list should act like a single tab stop. if (!(0, $b50b1cc8a843ace7$export$b9b3dfddab17db27)()) state.selectionManager.setFocusedKey(node.key); return; } }; let onKeyDown = (e)=>{ let activeElement = (0, $d8ac7ed472840322$export$cd4e5573fbe2b576)(); if (!(0, $d8ac7ed472840322$export$4282f70798064fe0)(e.currentTarget, (0, $d8ac7ed472840322$export$e58f029f0fbfdb29)(e)) || !ref.current || !activeElement) return; if (keyboardNavigationBehavior === 'tab') { // Stop propagation for all events that originate from the children of the gridlist item since we don't want to trigger // grid level interactions (row navigation/typeselect/etc) // exception made for Tab since that needs to propagate to useSelectableCollection to tab out of the gridlist, might be others? if ((0, $d8ac7ed472840322$export$e58f029f0fbfdb29)(e) !== ref.current && e.key !== 'Tab') { e.stopPropagation(); return; } if ($7d08662b45460e3b$var$handleTreeExpansionKeys(e, state, node, hasChildRows, direction, activeElement, ref.current)) return; } switch(e.key){ case 'Tab': if (keyboardNavigationBehavior === 'tab') { // If there is another focusable element within this item, stop propagation so the tab key // is handled by the browser and not by useSelectableCollection (which would take us out of the list). let walker = (0, $903814aeb7d53b38$export$2d6ec8fc375ceafa)(ref.current, { tabbable: true }); walker.currentNode = activeElement; let next = e.shiftKey ? walker.previousNode() : walker.nextNode(); if (next) e.stopPropagation(); } } }; let syntheticLinkProps = (0, $044d3c97ce5d6621$export$bdc77b0c0a3a85d6)(node.props); let linkProps = itemStates.hasAction ? syntheticLinkProps : {}; // TODO: re-add when we get translations and fix this for iOS VO // let rowAnnouncement; // if (onAction) { // rowAnnouncement = stringFormatter.format('hasActionAnnouncement'); // } else if (hasLink) { // rowAnnouncement = stringFormatter.format('hasLinkAnnouncement', { // link: node.props.href // }); // } let rowProps = (0, $64c36edd757dfa16$export$9d1611c77c2fe928)(itemProps, linkProps, { role: 'row', onKeyDownCapture: keyboardNavigationBehavior === 'arrow' ? onKeyDownCapture : undefined, onFocus: onFocus, // 'aria-label': [(node.textValue || undefined), rowAnnouncement].filter(Boolean).join(', '), 'aria-label': node['aria-label'] || node.textValue || undefined, 'aria-selected': state.selectionManager.canSelectItem(node.key) ? state.selectionManager.isSelected(node.key) : undefined, 'aria-disabled': state.selectionManager.isDisabled(node.key) || undefined, 'aria-labelledby': descriptionId && (node['aria-label'] || node.textValue) ? `${(0, $781ed2e01df48c52$export$f45c25170b9a99c2)(state, node.key)} ${descriptionId}` : undefined, id: (0, $781ed2e01df48c52$export$f45c25170b9a99c2)(state, node.key) }); // we need to guard against space/enter triggering selection/row link via usePress (from itemProps) so check if propagation // is stopped. this also fixes space not working in a textfield in a tree parent row let baseOnKeyDown = rowProps.onKeyDown; rowProps.onKeyDown = (e)=>{ onKeyDown(e); if (!e.isPropagationStopped()) baseOnKeyDown === null || baseOnKeyDown === void 0 ? void 0 : baseOnKeyDown(e); }; // guard against presses triggering row selecition when they happen on elements within the row // am currently assuming if it is tabbable it is interactive, but maybe can use a different kind of check let baseOnPointerDown = rowProps.onPointerDown; rowProps.onPointerDown = (e)=>{ let target = (0, $d8ac7ed472840322$export$e58f029f0fbfdb29)(e); if (target && target !== ref.current && (0, $ee5e22534121197a$export$bebd5a1431fec25d)(target)) { e.stopPropagation(); return; } baseOnPointerDown === null || baseOnPointerDown === void 0 ? void 0 : baseOnPointerDown(e); }; let baseOnMouseDown = rowProps.onMouseDown; rowProps.onMouseDown = (e)=>{ let target = (0, $d8ac7ed472840322$export$e58f029f0fbfdb29)(e); if (target && target !== ref.current && (0, $ee5e22534121197a$export$bebd5a1431fec25d)(target)) { e.stopPropagation(); return; } baseOnMouseDown === null || baseOnMouseDown === void 0 ? void 0 : baseOnMouseDown(e); }; if (isVirtualized) { let { collection: collection } = state; let nodes = [ ...collection ]; // TODO: refactor ListCollection to store an absolute index of a node's position? rowProps['aria-rowindex'] = nodes.find((node)=>node.type === 'section') ? [ ...collection.getKeys() ].filter((key)=>{ var _collection_getItem; return ((_collection_getItem = collection.getItem(key)) === null || _collection_getItem === void 0 ? void 0 : _collection_getItem.type) !== 'section'; }).findIndex((key)=>key === node.key) + 1 : node.index + 1; } let gridCellProps = { role: 'gridcell', 'aria-colindex': 1 }; // TODO: should isExpanded and hasChildRows be a item state that gets returned by the hook? return { rowProps: { ...(0, $64c36edd757dfa16$export$9d1611c77c2fe928)(rowProps, treeGridRowProps) }, gridCellProps: gridCellProps, descriptionProps: { id: descriptionId }, ...itemStates }; } function $7d08662b45460e3b$var$handleTreeExpansionKeys(e, state, node, hasChildRows, direction, activeElement, rowRef) { if (!('expandedKeys' in state) || activeElement !== rowRef) return false; if (e.key === $7d08662b45460e3b$var$EXPANSION_KEYS['expand'][direction] && state.selectionManager.focusedKey === node.key && hasChildRows && !state.expandedKeys.has(node.key)) { state.toggleKey(node.key); e.stopPropagation(); return true; } else if (e.key === $7d08662b45460e3b$var$EXPANSION_KEYS['collapse'][direction] && state.selectionManager.focusedKey === node.key) { var _state_collection_getItem; // If item is collapsible, collapse it; else move to parent if (hasChildRows && state.expandedKeys.has(node.key)) { state.toggleKey(node.key); e.stopPropagation(); return true; } else if (!state.expandedKeys.has(node.key) && node.parentKey && ((_state_collection_getItem = state.collection.getItem(node.parentKey)) === null || _state_collection_getItem === void 0 ? void 0 : _state_collection_getItem.type) === 'item') { // Item is a leaf or already collapsed, move focus to parent state.selectionManager.setFocusedKey(node.parentKey); e.stopPropagation(); return true; } } return false; } function $7d08662b45460e3b$var$last(walker) { let next = null; let last = null; do { last = walker.lastChild(); if (last) next = last; }while (last); return next; } function $7d08662b45460e3b$var$getDirectChildren(parent, collection) { var _collection_getChildren; // We can't assume that we can use firstChildKey because if a person builds a tree using hooks, they would not have access to that property (using type Node vs CollectionNode) // Instead, get all children and start at the first node (rather than just using firstChildKey) and only look at its siblings let children = (_collection_getChildren = collection.getChildren) === null || _collection_getChildren === void 0 ? void 0 : _collection_getChildren.call(collection, parent.key); let childArray = children ? Array.from(children) : []; let node = childArray.length > 0 ? childArray[0] : null; let siblings = []; while(node){ siblings.push(node); node = node.nextKey != null ? collection.getItem(node.nextKey) : null; } return siblings; } export {$7d08662b45460e3b$export$9610e69494fadfd2 as useGridListItem}; //# sourceMappingURL=useGridListItem.js.map