@react-aria/selection 3.23.1 → 3.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -54,6 +54,11 @@ export interface AriaSelectableCollectionOptions {
54
54
  * @default false
55
55
  */
56
56
  disallowSelectAll?: boolean,
57
+ /**
58
+ * Whether pressing the Escape should clear selection in the collection or not.
59
+ * @default 'clearSelection'
60
+ */
61
+ escapeKeyBehavior?: 'clearSelection' | 'none',
57
62
  /**
58
63
  * Whether selection should occur automatically on focus.
59
64
  * @default false
@@ -108,6 +113,7 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
108
113
  shouldFocusWrap = false,
109
114
  disallowEmptySelection = false,
110
115
  disallowSelectAll = false,
116
+ escapeKeyBehavior = 'clearSelection',
111
117
  selectOnFocus = manager.selectionBehavior === 'replace',
112
118
  disallowTypeAhead = false,
113
119
  shouldUseVirtualFocus,
@@ -279,7 +285,7 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
279
285
  }
280
286
  break;
281
287
  case 'Escape':
282
- if (!disallowEmptySelection && manager.selectedKeys.size !== 0) {
288
+ if (escapeKeyBehavior === 'clearSelection' && !disallowEmptySelection && manager.selectedKeys.size !== 0) {
283
289
  e.stopPropagation();
284
290
  e.preventDefault();
285
291
  manager.clearSelection();
@@ -499,6 +505,7 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
499
505
 
500
506
  // Scroll the focused element into view when the focusedKey changes.
501
507
  let lastFocusedKey = useRef(manager.focusedKey);
508
+ let raf = useRef<number | null>(null);
502
509
  useEffect(() => {
503
510
  if (manager.isFocused && manager.focusedKey != null && (manager.focusedKey !== lastFocusedKey.current || didAutoFocusRef.current) && scrollRef.current && ref.current) {
504
511
  let modality = getInteractionModality();
@@ -510,8 +517,16 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
510
517
  }
511
518
 
512
519
  if (modality === 'keyboard' || didAutoFocusRef.current) {
513
- scrollIntoView(scrollRef.current, element);
514
520
 
521
+ if (raf.current) {
522
+ cancelAnimationFrame(raf.current);
523
+ }
524
+
525
+ raf.current = requestAnimationFrame(() => {
526
+ if (scrollRef.current) {
527
+ scrollIntoView(scrollRef.current, element);
528
+ }
529
+ });
515
530
  // Avoid scroll in iOS VO, since it may cause overlay to close (i.e. RAC submenu)
516
531
  if (modality !== 'virtual') {
517
532
  scrollIntoViewport(element, {containingElement: ref.current});
@@ -528,6 +543,14 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
528
543
  didAutoFocusRef.current = false;
529
544
  });
530
545
 
546
+ useEffect(() => {
547
+ return () => {
548
+ if (raf.current) {
549
+ cancelAnimationFrame(raf.current);
550
+ }
551
+ };
552
+ }, []);
553
+
531
554
  // Intercept FocusScope restoration since virtualized collections can reuse DOM nodes.
532
555
  useEvent(ref, 'react-aria-focus-scope-restore', e => {
533
556
  e.preventDefault();
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import {DOMAttributes, DOMProps, FocusableElement, Key, LongPressEvent, PointerType, PressEvent, RefObject} from '@react-types/shared';
14
- import {focusSafely, PressProps, useLongPress, usePress} from '@react-aria/interactions';
14
+ import {focusSafely, PressHookProps, useLongPress, usePress} from '@react-aria/interactions';
15
15
  import {getCollectionId, isNonContiguousSelectionModifier} from './utils';
16
16
  import {isCtrlKeyPressed, mergeProps, openLink, useId, useRouter} from '@react-aria/utils';
17
17
  import {moveVirtualFocus} from '@react-aria/focus';
@@ -239,7 +239,7 @@ export function useSelectableItem(options: SelectableItemOptions): SelectableIte
239
239
  // we want to be able to have the pointer down on the trigger that opens the menu and
240
240
  // the pointer up on the menu item rather than requiring a separate press.
241
241
  // For keyboard events, selection still occurs on key down.
242
- let itemPressProps: PressProps = {};
242
+ let itemPressProps: PressHookProps = {ref};
243
243
  if (shouldSelectOnPressUp) {
244
244
  itemPressProps.onPressStart = (e) => {
245
245
  modality.current = e.pointerType;
@@ -382,7 +382,7 @@ export function useSelectableItem(options: SelectableItemOptions): SelectableIte
382
382
  return {
383
383
  itemProps: mergeProps(
384
384
  itemProps,
385
- allowsSelection || hasPrimaryAction || shouldUseVirtualFocus ? pressProps : {},
385
+ allowsSelection || hasPrimaryAction || (shouldUseVirtualFocus && !isDisabled) ? pressProps : {},
386
386
  longPressEnabled ? longPressProps : {},
387
387
  {onDoubleClick, onDragStartCapture, onClick, id},
388
388
  // Prevent DOM focus from moving on mouse down when using virtual focus
package/src/utils.ts CHANGED
@@ -20,13 +20,13 @@ interface Event {
20
20
  metaKey: boolean
21
21
  }
22
22
 
23
- export function isNonContiguousSelectionModifier(e: Event) {
23
+ export function isNonContiguousSelectionModifier(e: Event): boolean {
24
24
  // Ctrl + Arrow Up/Arrow Down has a system wide meaning on macOS, so use Alt instead.
25
25
  // On Windows and Ubuntu, Alt + Space has a system wide meaning.
26
26
  return isAppleDevice() ? e.altKey : e.ctrlKey;
27
27
  }
28
28
 
29
- export function getItemElement(collectionRef: RefObject<HTMLElement | null>, key: Key) {
29
+ export function getItemElement(collectionRef: RefObject<HTMLElement | null>, key: Key): Element | null | undefined {
30
30
  let selector = `[data-key="${CSS.escape(String(key))}"]`;
31
31
  let collection = collectionRef.current?.dataset.collection;
32
32
  if (collection) {
@@ -35,13 +35,13 @@ export function getItemElement(collectionRef: RefObject<HTMLElement | null>, key
35
35
  return collectionRef.current?.querySelector(selector);
36
36
  }
37
37
 
38
- const collectionMap = new WeakMap();
39
- export function useCollectionId(collection: Collection<any>) {
38
+ const collectionMap = new WeakMap<Collection<any>, string>();
39
+ export function useCollectionId(collection: Collection<any>): string {
40
40
  let id = useId();
41
41
  collectionMap.set(collection, id);
42
42
  return id;
43
43
  }
44
44
 
45
- export function getCollectionId(collection: Collection<any>) {
45
+ export function getCollectionId(collection: Collection<any>): string {
46
46
  return collectionMap.get(collection)!;
47
47
  }