331 lines
21 KiB
Plaintext
331 lines
21 KiB
Plaintext
var $ee0bdf4faa47f2a8$exports = require("./utils.main.js");
|
|
var $a1189052f36475e8$exports = require("./useTypeSelect.main.js");
|
|
var $bT8Bh$reactdom = require("react-dom");
|
|
var $bT8Bh$react = require("react");
|
|
var $bT8Bh$reactariafocus = require("@react-aria/focus");
|
|
var $bT8Bh$reactariautils = require("@react-aria/utils");
|
|
var $bT8Bh$reactariainteractions = require("@react-aria/interactions");
|
|
var $bT8Bh$reactariai18n = require("@react-aria/i18n");
|
|
|
|
|
|
function $parcel$export(e, n, v, s) {
|
|
Object.defineProperty(e, n, {get: v, set: s, enumerable: true, configurable: true});
|
|
}
|
|
|
|
$parcel$export(module.exports, "useSelectableCollection", () => $b6837c2f80a3c32f$export$d6daf82dcd84e87c);
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function $b6837c2f80a3c32f$export$d6daf82dcd84e87c(options) {
|
|
let { selectionManager: manager, keyboardDelegate: delegate, ref: ref, autoFocus: autoFocus = false, shouldFocusWrap: shouldFocusWrap = false, disallowEmptySelection: disallowEmptySelection = false, disallowSelectAll: disallowSelectAll = false, selectOnFocus: selectOnFocus = manager.selectionBehavior === 'replace', disallowTypeAhead: disallowTypeAhead = false, shouldUseVirtualFocus: shouldUseVirtualFocus, allowsTabNavigation: allowsTabNavigation = false, isVirtualized: isVirtualized, scrollRef: // If no scrollRef is provided, assume the collection ref is the scrollable region
|
|
scrollRef = ref, linkBehavior: linkBehavior = 'action' } = options;
|
|
let { direction: direction } = (0, $bT8Bh$reactariai18n.useLocale)();
|
|
let router = (0, $bT8Bh$reactariautils.useRouter)();
|
|
let onKeyDown = (e)=>{
|
|
var _ref_current;
|
|
// Prevent option + tab from doing anything since it doesn't move focus to the cells, only buttons/checkboxes
|
|
if (e.altKey && e.key === 'Tab') e.preventDefault();
|
|
// Keyboard events bubble through portals. Don't handle keyboard events
|
|
// for elements outside the collection (e.g. menus).
|
|
if (!((_ref_current = ref.current) === null || _ref_current === void 0 ? void 0 : _ref_current.contains(e.target))) return;
|
|
const navigateToKey = (key, childFocus)=>{
|
|
if (key != null) {
|
|
if (manager.isLink(key) && linkBehavior === 'selection' && selectOnFocus && !(0, $ee0bdf4faa47f2a8$exports.isNonContiguousSelectionModifier)(e)) {
|
|
var _scrollRef_current;
|
|
// Set focused key and re-render synchronously to bring item into view if needed.
|
|
(0, $bT8Bh$reactdom.flushSync)(()=>{
|
|
manager.setFocusedKey(key, childFocus);
|
|
});
|
|
let item = (_scrollRef_current = scrollRef.current) === null || _scrollRef_current === void 0 ? void 0 : _scrollRef_current.querySelector(`[data-key="${CSS.escape(key.toString())}"]`);
|
|
let itemProps = manager.getItemProps(key);
|
|
if (item) router.open(item, e, itemProps.href, itemProps.routerOptions);
|
|
return;
|
|
}
|
|
manager.setFocusedKey(key, childFocus);
|
|
if (manager.isLink(key) && linkBehavior === 'override') return;
|
|
if (e.shiftKey && manager.selectionMode === 'multiple') manager.extendSelection(key);
|
|
else if (selectOnFocus && !(0, $ee0bdf4faa47f2a8$exports.isNonContiguousSelectionModifier)(e)) manager.replaceSelection(key);
|
|
}
|
|
};
|
|
switch(e.key){
|
|
case 'ArrowDown':
|
|
if (delegate.getKeyBelow) {
|
|
var _delegate_getKeyBelow, _delegate_getFirstKey, _delegate_getFirstKey1;
|
|
let nextKey = manager.focusedKey != null ? (_delegate_getKeyBelow = delegate.getKeyBelow) === null || _delegate_getKeyBelow === void 0 ? void 0 : _delegate_getKeyBelow.call(delegate, manager.focusedKey) : (_delegate_getFirstKey = delegate.getFirstKey) === null || _delegate_getFirstKey === void 0 ? void 0 : _delegate_getFirstKey.call(delegate);
|
|
if (nextKey == null && shouldFocusWrap) nextKey = (_delegate_getFirstKey1 = delegate.getFirstKey) === null || _delegate_getFirstKey1 === void 0 ? void 0 : _delegate_getFirstKey1.call(delegate, manager.focusedKey);
|
|
if (nextKey != null) {
|
|
e.preventDefault();
|
|
navigateToKey(nextKey);
|
|
}
|
|
}
|
|
break;
|
|
case 'ArrowUp':
|
|
if (delegate.getKeyAbove) {
|
|
var _delegate_getKeyAbove, _delegate_getLastKey, _delegate_getLastKey1;
|
|
let nextKey = manager.focusedKey != null ? (_delegate_getKeyAbove = delegate.getKeyAbove) === null || _delegate_getKeyAbove === void 0 ? void 0 : _delegate_getKeyAbove.call(delegate, manager.focusedKey) : (_delegate_getLastKey = delegate.getLastKey) === null || _delegate_getLastKey === void 0 ? void 0 : _delegate_getLastKey.call(delegate);
|
|
if (nextKey == null && shouldFocusWrap) nextKey = (_delegate_getLastKey1 = delegate.getLastKey) === null || _delegate_getLastKey1 === void 0 ? void 0 : _delegate_getLastKey1.call(delegate, manager.focusedKey);
|
|
if (nextKey != null) {
|
|
e.preventDefault();
|
|
navigateToKey(nextKey);
|
|
}
|
|
}
|
|
break;
|
|
case 'ArrowLeft':
|
|
if (delegate.getKeyLeftOf) {
|
|
var _delegate_getKeyLeftOf, _delegate_getFirstKey2, _delegate_getLastKey2;
|
|
let nextKey = manager.focusedKey != null ? (_delegate_getKeyLeftOf = delegate.getKeyLeftOf) === null || _delegate_getKeyLeftOf === void 0 ? void 0 : _delegate_getKeyLeftOf.call(delegate, manager.focusedKey) : null;
|
|
if (nextKey == null && shouldFocusWrap) nextKey = direction === 'rtl' ? (_delegate_getFirstKey2 = delegate.getFirstKey) === null || _delegate_getFirstKey2 === void 0 ? void 0 : _delegate_getFirstKey2.call(delegate, manager.focusedKey) : (_delegate_getLastKey2 = delegate.getLastKey) === null || _delegate_getLastKey2 === void 0 ? void 0 : _delegate_getLastKey2.call(delegate, manager.focusedKey);
|
|
if (nextKey != null) {
|
|
e.preventDefault();
|
|
navigateToKey(nextKey, direction === 'rtl' ? 'first' : 'last');
|
|
}
|
|
}
|
|
break;
|
|
case 'ArrowRight':
|
|
if (delegate.getKeyRightOf) {
|
|
var _delegate_getKeyRightOf, _delegate_getLastKey3, _delegate_getFirstKey3;
|
|
let nextKey = manager.focusedKey != null ? (_delegate_getKeyRightOf = delegate.getKeyRightOf) === null || _delegate_getKeyRightOf === void 0 ? void 0 : _delegate_getKeyRightOf.call(delegate, manager.focusedKey) : null;
|
|
if (nextKey == null && shouldFocusWrap) nextKey = direction === 'rtl' ? (_delegate_getLastKey3 = delegate.getLastKey) === null || _delegate_getLastKey3 === void 0 ? void 0 : _delegate_getLastKey3.call(delegate, manager.focusedKey) : (_delegate_getFirstKey3 = delegate.getFirstKey) === null || _delegate_getFirstKey3 === void 0 ? void 0 : _delegate_getFirstKey3.call(delegate, manager.focusedKey);
|
|
if (nextKey != null) {
|
|
e.preventDefault();
|
|
navigateToKey(nextKey, direction === 'rtl' ? 'last' : 'first');
|
|
}
|
|
}
|
|
break;
|
|
case 'Home':
|
|
if (delegate.getFirstKey) {
|
|
e.preventDefault();
|
|
let firstKey = delegate.getFirstKey(manager.focusedKey, (0, $ee0bdf4faa47f2a8$exports.isCtrlKeyPressed)(e));
|
|
manager.setFocusedKey(firstKey);
|
|
if (firstKey != null) {
|
|
if ((0, $ee0bdf4faa47f2a8$exports.isCtrlKeyPressed)(e) && e.shiftKey && manager.selectionMode === 'multiple') manager.extendSelection(firstKey);
|
|
else if (selectOnFocus) manager.replaceSelection(firstKey);
|
|
}
|
|
}
|
|
break;
|
|
case 'End':
|
|
if (delegate.getLastKey) {
|
|
e.preventDefault();
|
|
let lastKey = delegate.getLastKey(manager.focusedKey, (0, $ee0bdf4faa47f2a8$exports.isCtrlKeyPressed)(e));
|
|
manager.setFocusedKey(lastKey);
|
|
if (lastKey != null) {
|
|
if ((0, $ee0bdf4faa47f2a8$exports.isCtrlKeyPressed)(e) && e.shiftKey && manager.selectionMode === 'multiple') manager.extendSelection(lastKey);
|
|
else if (selectOnFocus) manager.replaceSelection(lastKey);
|
|
}
|
|
}
|
|
break;
|
|
case 'PageDown':
|
|
if (delegate.getKeyPageBelow && manager.focusedKey != null) {
|
|
let nextKey = delegate.getKeyPageBelow(manager.focusedKey);
|
|
if (nextKey != null) {
|
|
e.preventDefault();
|
|
navigateToKey(nextKey);
|
|
}
|
|
}
|
|
break;
|
|
case 'PageUp':
|
|
if (delegate.getKeyPageAbove && manager.focusedKey != null) {
|
|
let nextKey = delegate.getKeyPageAbove(manager.focusedKey);
|
|
if (nextKey != null) {
|
|
e.preventDefault();
|
|
navigateToKey(nextKey);
|
|
}
|
|
}
|
|
break;
|
|
case 'a':
|
|
if ((0, $ee0bdf4faa47f2a8$exports.isCtrlKeyPressed)(e) && manager.selectionMode === 'multiple' && disallowSelectAll !== true) {
|
|
e.preventDefault();
|
|
manager.selectAll();
|
|
}
|
|
break;
|
|
case 'Escape':
|
|
if (!disallowEmptySelection && manager.selectedKeys.size !== 0) {
|
|
e.stopPropagation();
|
|
e.preventDefault();
|
|
manager.clearSelection();
|
|
}
|
|
break;
|
|
case 'Tab':
|
|
if (!allowsTabNavigation) {
|
|
// There may be elements that are "tabbable" inside a collection (e.g. in a grid cell).
|
|
// However, collections should be treated as a single tab stop, with arrow key navigation internally.
|
|
// We don't control the rendering of these, so we can't override the tabIndex to prevent tabbing.
|
|
// Instead, we handle the Tab key, and move focus manually to the first/last tabbable element
|
|
// in the collection, so that the browser default behavior will apply starting from that element
|
|
// rather than the currently focused one.
|
|
if (e.shiftKey) ref.current.focus();
|
|
else {
|
|
let walker = (0, $bT8Bh$reactariafocus.getFocusableTreeWalker)(ref.current, {
|
|
tabbable: true
|
|
});
|
|
let next = undefined;
|
|
let last;
|
|
do {
|
|
last = walker.lastChild();
|
|
if (last) next = last;
|
|
}while (last);
|
|
if (next && !next.contains(document.activeElement)) (0, $bT8Bh$reactariautils.focusWithoutScrolling)(next);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
// Store the scroll position so we can restore it later.
|
|
/// TODO: should this happen all the time??
|
|
let scrollPos = (0, $bT8Bh$react.useRef)({
|
|
top: 0,
|
|
left: 0
|
|
});
|
|
(0, $bT8Bh$reactariautils.useEvent)(scrollRef, 'scroll', isVirtualized ? undefined : ()=>{
|
|
var _scrollRef_current, _scrollRef_current1;
|
|
var _scrollRef_current_scrollTop, _scrollRef_current_scrollLeft;
|
|
scrollPos.current = {
|
|
top: (_scrollRef_current_scrollTop = (_scrollRef_current = scrollRef.current) === null || _scrollRef_current === void 0 ? void 0 : _scrollRef_current.scrollTop) !== null && _scrollRef_current_scrollTop !== void 0 ? _scrollRef_current_scrollTop : 0,
|
|
left: (_scrollRef_current_scrollLeft = (_scrollRef_current1 = scrollRef.current) === null || _scrollRef_current1 === void 0 ? void 0 : _scrollRef_current1.scrollLeft) !== null && _scrollRef_current_scrollLeft !== void 0 ? _scrollRef_current_scrollLeft : 0
|
|
};
|
|
});
|
|
let onFocus = (e)=>{
|
|
if (manager.isFocused) {
|
|
// If a focus event bubbled through a portal, reset focus state.
|
|
if (!e.currentTarget.contains(e.target)) manager.setFocused(false);
|
|
return;
|
|
}
|
|
// Focus events can bubble through portals. Ignore these events.
|
|
if (!e.currentTarget.contains(e.target)) return;
|
|
manager.setFocused(true);
|
|
if (manager.focusedKey == null) {
|
|
var _delegate_getLastKey, _delegate_getFirstKey;
|
|
let navigateToFirstKey = (key)=>{
|
|
if (key != null) {
|
|
manager.setFocusedKey(key);
|
|
if (selectOnFocus) manager.replaceSelection(key);
|
|
}
|
|
};
|
|
// If the user hasn't yet interacted with the collection, there will be no focusedKey set.
|
|
// Attempt to detect whether the user is tabbing forward or backward into the collection
|
|
// and either focus the first or last item accordingly.
|
|
let relatedTarget = e.relatedTarget;
|
|
var _manager_lastSelectedKey, _manager_firstSelectedKey;
|
|
if (relatedTarget && e.currentTarget.compareDocumentPosition(relatedTarget) & Node.DOCUMENT_POSITION_FOLLOWING) navigateToFirstKey((_manager_lastSelectedKey = manager.lastSelectedKey) !== null && _manager_lastSelectedKey !== void 0 ? _manager_lastSelectedKey : (_delegate_getLastKey = delegate.getLastKey) === null || _delegate_getLastKey === void 0 ? void 0 : _delegate_getLastKey.call(delegate));
|
|
else navigateToFirstKey((_manager_firstSelectedKey = manager.firstSelectedKey) !== null && _manager_firstSelectedKey !== void 0 ? _manager_firstSelectedKey : (_delegate_getFirstKey = delegate.getFirstKey) === null || _delegate_getFirstKey === void 0 ? void 0 : _delegate_getFirstKey.call(delegate));
|
|
} else if (!isVirtualized && scrollRef.current) {
|
|
// Restore the scroll position to what it was before.
|
|
scrollRef.current.scrollTop = scrollPos.current.top;
|
|
scrollRef.current.scrollLeft = scrollPos.current.left;
|
|
}
|
|
if (manager.focusedKey != null && scrollRef.current) {
|
|
// Refocus and scroll the focused item into view if it exists within the scrollable region.
|
|
let element = scrollRef.current.querySelector(`[data-key="${CSS.escape(manager.focusedKey.toString())}"]`);
|
|
if (element) {
|
|
// This prevents a flash of focus on the first/last element in the collection, or the collection itself.
|
|
if (!element.contains(document.activeElement)) (0, $bT8Bh$reactariautils.focusWithoutScrolling)(element);
|
|
let modality = (0, $bT8Bh$reactariainteractions.getInteractionModality)();
|
|
if (modality === 'keyboard') (0, $bT8Bh$reactariautils.scrollIntoViewport)(element, {
|
|
containingElement: ref.current
|
|
});
|
|
}
|
|
}
|
|
};
|
|
let onBlur = (e)=>{
|
|
// Don't set blurred and then focused again if moving focus within the collection.
|
|
if (!e.currentTarget.contains(e.relatedTarget)) manager.setFocused(false);
|
|
};
|
|
const autoFocusRef = (0, $bT8Bh$react.useRef)(autoFocus);
|
|
(0, $bT8Bh$react.useEffect)(()=>{
|
|
if (autoFocusRef.current) {
|
|
var _delegate_getFirstKey, _delegate_getLastKey;
|
|
let focusedKey = null;
|
|
var _delegate_getFirstKey1;
|
|
// Check focus strategy to determine which item to focus
|
|
if (autoFocus === 'first') focusedKey = (_delegate_getFirstKey1 = (_delegate_getFirstKey = delegate.getFirstKey) === null || _delegate_getFirstKey === void 0 ? void 0 : _delegate_getFirstKey.call(delegate)) !== null && _delegate_getFirstKey1 !== void 0 ? _delegate_getFirstKey1 : null;
|
|
var _delegate_getLastKey1;
|
|
if (autoFocus === 'last') focusedKey = (_delegate_getLastKey1 = (_delegate_getLastKey = delegate.getLastKey) === null || _delegate_getLastKey === void 0 ? void 0 : _delegate_getLastKey.call(delegate)) !== null && _delegate_getLastKey1 !== void 0 ? _delegate_getLastKey1 : null;
|
|
// If there are any selected keys, make the first one the new focus target
|
|
let selectedKeys = manager.selectedKeys;
|
|
if (selectedKeys.size) {
|
|
for (let key of selectedKeys)if (manager.canSelectItem(key)) {
|
|
focusedKey = key;
|
|
break;
|
|
}
|
|
}
|
|
manager.setFocused(true);
|
|
manager.setFocusedKey(focusedKey);
|
|
// If no default focus key is selected, focus the collection itself.
|
|
if (focusedKey == null && !shouldUseVirtualFocus && ref.current) (0, $bT8Bh$reactariafocus.focusSafely)(ref.current);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
// Scroll the focused element into view when the focusedKey changes.
|
|
let lastFocusedKey = (0, $bT8Bh$react.useRef)(manager.focusedKey);
|
|
(0, $bT8Bh$react.useEffect)(()=>{
|
|
if (manager.isFocused && manager.focusedKey != null && (manager.focusedKey !== lastFocusedKey.current || autoFocusRef.current) && scrollRef.current && ref.current) {
|
|
let modality = (0, $bT8Bh$reactariainteractions.getInteractionModality)();
|
|
let element = ref.current.querySelector(`[data-key="${CSS.escape(manager.focusedKey.toString())}"]`);
|
|
if (!element) // If item element wasn't found, return early (don't update autoFocusRef and lastFocusedKey).
|
|
// The collection may initially be empty (e.g. virtualizer), so wait until the element exists.
|
|
return;
|
|
if (modality === 'keyboard' || autoFocusRef.current) {
|
|
(0, $bT8Bh$reactariautils.scrollIntoView)(scrollRef.current, element);
|
|
// Avoid scroll in iOS VO, since it may cause overlay to close (i.e. RAC submenu)
|
|
if (modality !== 'virtual') (0, $bT8Bh$reactariautils.scrollIntoViewport)(element, {
|
|
containingElement: ref.current
|
|
});
|
|
}
|
|
}
|
|
// If the focused key becomes null (e.g. the last item is deleted), focus the whole collection.
|
|
if (!shouldUseVirtualFocus && manager.isFocused && manager.focusedKey == null && lastFocusedKey.current != null && ref.current) (0, $bT8Bh$reactariafocus.focusSafely)(ref.current);
|
|
lastFocusedKey.current = manager.focusedKey;
|
|
autoFocusRef.current = false;
|
|
});
|
|
// Intercept FocusScope restoration since virtualized collections can reuse DOM nodes.
|
|
(0, $bT8Bh$reactariautils.useEvent)(ref, 'react-aria-focus-scope-restore', (e)=>{
|
|
e.preventDefault();
|
|
manager.setFocused(true);
|
|
});
|
|
let handlers = {
|
|
onKeyDown: onKeyDown,
|
|
onFocus: onFocus,
|
|
onBlur: onBlur,
|
|
onMouseDown (e) {
|
|
// Ignore events that bubbled through portals.
|
|
if (scrollRef.current === e.target) // Prevent focus going to the collection when clicking on the scrollbar.
|
|
e.preventDefault();
|
|
}
|
|
};
|
|
let { typeSelectProps: typeSelectProps } = (0, $a1189052f36475e8$exports.useTypeSelect)({
|
|
keyboardDelegate: delegate,
|
|
selectionManager: manager
|
|
});
|
|
if (!disallowTypeAhead) handlers = (0, $bT8Bh$reactariautils.mergeProps)(typeSelectProps, handlers);
|
|
// If nothing is focused within the collection, make the collection itself tabbable.
|
|
// This will be marshalled to either the first or last item depending on where focus came from.
|
|
// If using virtual focus, don't set a tabIndex at all so that VoiceOver on iOS 14 doesn't try
|
|
// to move real DOM focus to the element anyway.
|
|
let tabIndex = undefined;
|
|
if (!shouldUseVirtualFocus) tabIndex = manager.focusedKey == null ? 0 : -1;
|
|
return {
|
|
collectionProps: {
|
|
...handlers,
|
|
tabIndex: tabIndex
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
//# sourceMappingURL=useSelectableCollection.main.js.map
|