@react-aria/selection 3.14.0 → 3.16.0

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.
package/dist/import.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {useRef as $eCAIO$useRef, useEffect as $eCAIO$useEffect, useMemo as $eCAIO$useMemo} from "react";
2
2
  import {getFocusableTreeWalker as $eCAIO$getFocusableTreeWalker, focusSafely as $eCAIO$focusSafely} from "@react-aria/focus";
3
- import {focusWithoutScrolling as $eCAIO$focusWithoutScrolling, useEvent as $eCAIO$useEvent, scrollIntoView as $eCAIO$scrollIntoView, scrollIntoViewport as $eCAIO$scrollIntoViewport, mergeProps as $eCAIO$mergeProps, isAppleDevice as $eCAIO$isAppleDevice, isMac as $eCAIO$isMac} from "@react-aria/utils";
3
+ import {focusWithoutScrolling as $eCAIO$focusWithoutScrolling, useEvent as $eCAIO$useEvent, scrollIntoViewport as $eCAIO$scrollIntoViewport, scrollIntoView as $eCAIO$scrollIntoView, mergeProps as $eCAIO$mergeProps, isAppleDevice as $eCAIO$isAppleDevice, isMac as $eCAIO$isMac, isScrollable as $eCAIO$isScrollable} from "@react-aria/utils";
4
4
  import {getInteractionModality as $eCAIO$getInteractionModality, usePress as $eCAIO$usePress, useLongPress as $eCAIO$useLongPress} from "@react-aria/interactions";
5
5
  import {useLocale as $eCAIO$useLocale, useCollator as $eCAIO$useCollator} from "@react-aria/i18n";
6
6
 
@@ -39,7 +39,6 @@ import {useLocale as $eCAIO$useLocale, useCollator as $eCAIO$useCollator} from "
39
39
  * OF ANY KIND, either express or implied. See the License for the specific language
40
40
  * governing permissions and limitations under the License.
41
41
  */
42
-
43
42
  function $feb5ffebff200149$export$d3e3bd3e26688c04(e) {
44
43
  // Ctrl + Arrow Up/Arrow Down has a system wide meaning on macOS, so use Alt instead.
45
44
  // On Windows and Ubuntu, Alt + Space has a system wide meaning.
@@ -74,7 +73,7 @@ function $fb3050f43d946246$export$e32c88dfddc6e1d8(options) {
74
73
  }).current;
75
74
  let onKeyDown = (e)=>{
76
75
  let character = $fb3050f43d946246$var$getStringForKey(e.key);
77
- if (!character || e.ctrlKey || e.metaKey) return;
76
+ if (!character || e.ctrlKey || e.metaKey || !e.currentTarget.contains(e.target)) return;
78
77
  // Do not propagate the Spacebar event if it's meant to be part of the search.
79
78
  // When we time out, the search term becomes empty, hence the check on length.
80
79
  // Trimming is to account for the case of pressing the Spacebar more than once,
@@ -147,23 +146,27 @@ function $ae20dd8cbca75726$export$d6daf82dcd84e87c(options) {
147
146
  if (delegate.getKeyAbove) {
148
147
  var _delegate_getLastKey, _delegate_getLastKey1;
149
148
  e.preventDefault();
150
- let nextKey1 = manager.focusedKey != null ? delegate.getKeyAbove(manager.focusedKey) : (_delegate_getLastKey = delegate.getLastKey) === null || _delegate_getLastKey === void 0 ? void 0 : _delegate_getLastKey.call(delegate);
151
- if (nextKey1 == null && shouldFocusWrap) nextKey1 = (_delegate_getLastKey1 = delegate.getLastKey) === null || _delegate_getLastKey1 === void 0 ? void 0 : _delegate_getLastKey1.call(delegate, manager.focusedKey);
152
- navigateToKey(nextKey1);
149
+ let nextKey = manager.focusedKey != null ? delegate.getKeyAbove(manager.focusedKey) : (_delegate_getLastKey = delegate.getLastKey) === null || _delegate_getLastKey === void 0 ? void 0 : _delegate_getLastKey.call(delegate);
150
+ if (nextKey == null && shouldFocusWrap) nextKey = (_delegate_getLastKey1 = delegate.getLastKey) === null || _delegate_getLastKey1 === void 0 ? void 0 : _delegate_getLastKey1.call(delegate, manager.focusedKey);
151
+ navigateToKey(nextKey);
153
152
  }
154
153
  break;
155
154
  case "ArrowLeft":
156
155
  if (delegate.getKeyLeftOf) {
156
+ var _delegate_getFirstKey2, _delegate_getLastKey2;
157
157
  e.preventDefault();
158
- let nextKey2 = delegate.getKeyLeftOf(manager.focusedKey);
159
- navigateToKey(nextKey2, direction === "rtl" ? "first" : "last");
158
+ let nextKey = delegate.getKeyLeftOf(manager.focusedKey);
159
+ 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);
160
+ navigateToKey(nextKey, direction === "rtl" ? "first" : "last");
160
161
  }
161
162
  break;
162
163
  case "ArrowRight":
163
164
  if (delegate.getKeyRightOf) {
165
+ var _delegate_getLastKey3, _delegate_getFirstKey3;
164
166
  e.preventDefault();
165
- let nextKey3 = delegate.getKeyRightOf(manager.focusedKey);
166
- navigateToKey(nextKey3, direction === "rtl" ? "last" : "first");
167
+ let nextKey = delegate.getKeyRightOf(manager.focusedKey);
168
+ 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);
169
+ navigateToKey(nextKey, direction === "rtl" ? "last" : "first");
167
170
  }
168
171
  break;
169
172
  case "Home":
@@ -187,15 +190,15 @@ function $ae20dd8cbca75726$export$d6daf82dcd84e87c(options) {
187
190
  case "PageDown":
188
191
  if (delegate.getKeyPageBelow) {
189
192
  e.preventDefault();
190
- let nextKey4 = delegate.getKeyPageBelow(manager.focusedKey);
191
- navigateToKey(nextKey4);
193
+ let nextKey = delegate.getKeyPageBelow(manager.focusedKey);
194
+ navigateToKey(nextKey);
192
195
  }
193
196
  break;
194
197
  case "PageUp":
195
198
  if (delegate.getKeyPageAbove) {
196
199
  e.preventDefault();
197
- let nextKey5 = delegate.getKeyPageAbove(manager.focusedKey);
198
- navigateToKey(nextKey5);
200
+ let nextKey = delegate.getKeyPageAbove(manager.focusedKey);
201
+ navigateToKey(nextKey);
199
202
  }
200
203
  break;
201
204
  case "a":
@@ -278,7 +281,10 @@ function $ae20dd8cbca75726$export$d6daf82dcd84e87c(options) {
278
281
  if (element) {
279
282
  // This prevents a flash of focus on the first/last element in the collection, or the collection itself.
280
283
  (0, $eCAIO$focusWithoutScrolling)(element);
281
- (0, $eCAIO$scrollIntoView)(scrollRef.current, element);
284
+ let modality = (0, $eCAIO$getInteractionModality)();
285
+ if (modality === "keyboard") (0, $eCAIO$scrollIntoViewport)(element, {
286
+ containingElement: ref.current
287
+ });
282
288
  }
283
289
  }
284
290
  };
@@ -306,17 +312,21 @@ function $ae20dd8cbca75726$export$d6daf82dcd84e87c(options) {
306
312
  }, []);
307
313
  // If not virtualized, scroll the focused element into view when the focusedKey changes.
308
314
  // When virtualized, Virtualizer handles this internally.
315
+ let lastFocusedKey = (0, $eCAIO$useRef)(manager.focusedKey);
309
316
  (0, $eCAIO$useEffect)(()=>{
310
317
  let modality = (0, $eCAIO$getInteractionModality)();
311
- if (!isVirtualized && manager.isFocused && manager.focusedKey != null && (scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current)) {
318
+ if (manager.isFocused && manager.focusedKey != null && (scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current)) {
312
319
  let element = scrollRef.current.querySelector(`[data-key="${manager.focusedKey}"]`);
313
- if (element) {
314
- (0, $eCAIO$scrollIntoView)(scrollRef.current, element);
315
- if (modality === "keyboard") (0, $eCAIO$scrollIntoViewport)(element, {
320
+ if (element && modality === "keyboard") {
321
+ if (!isVirtualized) (0, $eCAIO$scrollIntoView)(scrollRef.current, element);
322
+ (0, $eCAIO$scrollIntoViewport)(element, {
316
323
  containingElement: ref.current
317
324
  });
318
325
  }
319
326
  }
327
+ // If the focused key becomes null (e.g. the last item is deleted), focus the whole collection.
328
+ if (manager.isFocused && manager.focusedKey == null && lastFocusedKey.current != null) (0, $eCAIO$focusSafely)(ref.current);
329
+ lastFocusedKey.current = manager.focusedKey;
320
330
  }, [
321
331
  isVirtualized,
322
332
  scrollRef,
@@ -370,7 +380,7 @@ function $ae20dd8cbca75726$export$d6daf82dcd84e87c(options) {
370
380
 
371
381
 
372
382
  function $880e95eb8b93ba9a$export$ecf600387e221c37(options) {
373
- let { selectionManager: manager , key: key , ref: ref , shouldSelectOnPressUp: shouldSelectOnPressUp , isVirtualized: isVirtualized , shouldUseVirtualFocus: shouldUseVirtualFocus , focus: focus , isDisabled: isDisabled , onAction: onAction , allowsDifferentPressOrigin: allowsDifferentPressOrigin } = options;
383
+ let { selectionManager: manager , key: key , ref: ref , shouldSelectOnPressUp: shouldSelectOnPressUp , shouldUseVirtualFocus: shouldUseVirtualFocus , focus: focus , isDisabled: isDisabled , onAction: onAction , allowsDifferentPressOrigin: allowsDifferentPressOrigin } = options;
374
384
  let onSelect = (e)=>{
375
385
  if (e.pointerType === "keyboard" && (0, $feb5ffebff200149$export$d3e3bd3e26688c04)(e)) manager.toggleSelection(key);
376
386
  else {
@@ -476,7 +486,7 @@ function $880e95eb8b93ba9a$export$ecf600387e221c37(options) {
476
486
  }
477
487
  };
478
488
  }
479
- if (!isVirtualized) itemProps["data-key"] = key;
489
+ itemProps["data-key"] = key;
480
490
  itemPressProps.preventFocusOnPress = shouldUseVirtualFocus;
481
491
  let { pressProps: pressProps , isPressed: isPressed } = (0, $eCAIO$usePress)(itemPressProps);
482
492
  // Double clicking with a mouse with selectionBehavior = 'replace' performs an action.
@@ -550,8 +560,9 @@ function $880e95eb8b93ba9a$var$isSelectionKey() {
550
560
  * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
551
561
  * OF ANY KIND, either express or implied. See the License for the specific language
552
562
  * governing permissions and limitations under the License.
553
- */ class $2a25aae57d74318e$export$a05409b8bb224a5a {
554
- getKeyBelow(key) {
563
+ */
564
+ class $2a25aae57d74318e$export$a05409b8bb224a5a {
565
+ getNextKey(key) {
555
566
  key = this.collection.getKeyAfter(key);
556
567
  while(key != null){
557
568
  let item = this.collection.getItem(key);
@@ -560,7 +571,7 @@ function $880e95eb8b93ba9a$var$isSelectionKey() {
560
571
  }
561
572
  return null;
562
573
  }
563
- getKeyAbove(key) {
574
+ getPreviousKey(key) {
564
575
  key = this.collection.getKeyBefore(key);
565
576
  while(key != null){
566
577
  let item = this.collection.getItem(key);
@@ -569,6 +580,48 @@ function $880e95eb8b93ba9a$var$isSelectionKey() {
569
580
  }
570
581
  return null;
571
582
  }
583
+ findKey(key, nextKey, shouldSkip) {
584
+ let item = this.getItem(key);
585
+ if (!item) return null;
586
+ // Find the item above or below in the same column.
587
+ let prevRect = item.getBoundingClientRect();
588
+ do {
589
+ key = nextKey(key);
590
+ item = this.getItem(key);
591
+ }while (item && shouldSkip(prevRect, item.getBoundingClientRect()));
592
+ return key;
593
+ }
594
+ isSameRow(prevRect, itemRect) {
595
+ return prevRect.top === itemRect.top || prevRect.left !== itemRect.left;
596
+ }
597
+ isSameColumn(prevRect, itemRect) {
598
+ return prevRect.left === itemRect.left || prevRect.top !== itemRect.top;
599
+ }
600
+ getKeyBelow(key) {
601
+ if (this.layout === "grid" && this.orientation === "vertical") return this.findKey(key, (key)=>this.getNextKey(key), this.isSameRow);
602
+ else return this.getNextKey(key);
603
+ }
604
+ getKeyAbove(key) {
605
+ if (this.layout === "grid" && this.orientation === "vertical") return this.findKey(key, (key)=>this.getPreviousKey(key), this.isSameRow);
606
+ else return this.getPreviousKey(key);
607
+ }
608
+ getNextColumn(key, right) {
609
+ return right ? this.getPreviousKey(key) : this.getNextKey(key);
610
+ }
611
+ getKeyRightOf(key) {
612
+ if (this.layout === "grid") {
613
+ if (this.orientation === "vertical") return this.getNextColumn(key, this.direction === "rtl");
614
+ else return this.findKey(key, (key)=>this.getNextColumn(key, this.direction === "rtl"), this.isSameColumn);
615
+ } else if (this.orientation === "horizontal") return this.getNextColumn(key, this.direction === "rtl");
616
+ return null;
617
+ }
618
+ getKeyLeftOf(key) {
619
+ if (this.layout === "grid") {
620
+ if (this.orientation === "vertical") return this.getNextColumn(key, this.direction === "ltr");
621
+ else return this.findKey(key, (key)=>this.getNextColumn(key, this.direction === "ltr"), this.isSameColumn);
622
+ } else if (this.orientation === "horizontal") return this.getNextColumn(key, this.direction === "ltr");
623
+ return null;
624
+ }
572
625
  getFirstKey() {
573
626
  let key = this.collection.getFirstKey();
574
627
  while(key != null){
@@ -594,23 +647,53 @@ function $880e95eb8b93ba9a$var$isSelectionKey() {
594
647
  let menu = this.ref.current;
595
648
  let item = this.getItem(key);
596
649
  if (!item) return null;
597
- let pageY = Math.max(0, item.offsetTop + item.offsetHeight - menu.offsetHeight);
598
- while(item && item.offsetTop > pageY){
599
- key = this.getKeyAbove(key);
600
- item = key == null ? null : this.getItem(key);
650
+ if (!(0, $eCAIO$isScrollable)(menu)) return this.getFirstKey();
651
+ let containerRect = menu.getBoundingClientRect();
652
+ let itemRect = item.getBoundingClientRect();
653
+ if (this.orientation === "horizontal") {
654
+ let containerX = containerRect.x - menu.scrollLeft;
655
+ let pageX = Math.max(0, itemRect.x - containerX + itemRect.width - containerRect.width);
656
+ while(item && itemRect.x - containerX > pageX){
657
+ key = this.getKeyAbove(key);
658
+ item = key == null ? null : this.getItem(key);
659
+ itemRect = item === null || item === void 0 ? void 0 : item.getBoundingClientRect();
660
+ }
661
+ } else {
662
+ let containerY = containerRect.y - menu.scrollTop;
663
+ let pageY = Math.max(0, itemRect.y - containerY + itemRect.height - containerRect.height);
664
+ while(item && itemRect.y - containerY > pageY){
665
+ key = this.getKeyAbove(key);
666
+ item = key == null ? null : this.getItem(key);
667
+ itemRect = item === null || item === void 0 ? void 0 : item.getBoundingClientRect();
668
+ }
601
669
  }
602
- return key;
670
+ return key !== null && key !== void 0 ? key : this.getFirstKey();
603
671
  }
604
672
  getKeyPageBelow(key) {
605
673
  let menu = this.ref.current;
606
674
  let item = this.getItem(key);
607
675
  if (!item) return null;
608
- let pageY = Math.min(menu.scrollHeight, item.offsetTop - item.offsetHeight + menu.offsetHeight);
609
- while(item && item.offsetTop < pageY){
610
- key = this.getKeyBelow(key);
611
- item = key == null ? null : this.getItem(key);
676
+ if (!(0, $eCAIO$isScrollable)(menu)) return this.getLastKey();
677
+ let containerRect = menu.getBoundingClientRect();
678
+ let itemRect = item.getBoundingClientRect();
679
+ if (this.orientation === "horizontal") {
680
+ let containerX = containerRect.x - menu.scrollLeft;
681
+ let pageX = Math.min(menu.scrollWidth, itemRect.x - containerX - itemRect.width + containerRect.width);
682
+ while(item && itemRect.x - containerX < pageX){
683
+ key = this.getKeyBelow(key);
684
+ item = key == null ? null : this.getItem(key);
685
+ itemRect = item === null || item === void 0 ? void 0 : item.getBoundingClientRect();
686
+ }
687
+ } else {
688
+ let containerY = containerRect.y - menu.scrollTop;
689
+ let pageY = Math.min(menu.scrollHeight, itemRect.y - containerY - itemRect.height + containerRect.height);
690
+ while(item && itemRect.y - containerY < pageY){
691
+ key = this.getKeyBelow(key);
692
+ item = key == null ? null : this.getItem(key);
693
+ itemRect = item === null || item === void 0 ? void 0 : item.getBoundingClientRect();
694
+ }
612
695
  }
613
- return key;
696
+ return key !== null && key !== void 0 ? key : this.getLastKey();
614
697
  }
615
698
  getKeyForSearch(search, fromKey) {
616
699
  if (!this.collator) return null;
@@ -624,11 +707,29 @@ function $880e95eb8b93ba9a$var$isSelectionKey() {
624
707
  }
625
708
  return null;
626
709
  }
627
- constructor(collection, disabledKeys, ref, collator){
628
- this.collection = collection;
629
- this.disabledKeys = disabledKeys;
630
- this.ref = ref;
631
- this.collator = collator;
710
+ constructor(...args){
711
+ if (args.length === 1) {
712
+ let opts = args[0];
713
+ this.collection = opts.collection;
714
+ this.ref = opts.ref;
715
+ this.disabledKeys = opts.disabledKeys || new Set();
716
+ this.orientation = opts.orientation;
717
+ this.direction = opts.direction;
718
+ this.layout = opts.layout || "stack";
719
+ } else {
720
+ this.collection = args[0];
721
+ this.disabledKeys = args[1];
722
+ this.ref = args[2];
723
+ this.collator = args[3];
724
+ this.layout = "stack";
725
+ this.orientation = "vertical";
726
+ }
727
+ // If this is a vertical stack, remove the left/right methods completely
728
+ // so they aren't called by useDroppableCollection.
729
+ if (this.layout === "stack" && this.orientation === "vertical") {
730
+ this.getKeyLeftOf = undefined;
731
+ this.getKeyRightOf = undefined;
732
+ }
632
733
  }
633
734
  }
634
735
 
package/dist/main.js CHANGED
@@ -48,7 +48,6 @@ $parcel$export(module.exports, "useTypeSelect", () => $a1189052f36475e8$export$e
48
48
  * OF ANY KIND, either express or implied. See the License for the specific language
49
49
  * governing permissions and limitations under the License.
50
50
  */
51
-
52
51
  function $ee0bdf4faa47f2a8$export$d3e3bd3e26688c04(e) {
53
52
  // Ctrl + Arrow Up/Arrow Down has a system wide meaning on macOS, so use Alt instead.
54
53
  // On Windows and Ubuntu, Alt + Space has a system wide meaning.
@@ -83,7 +82,7 @@ function $a1189052f36475e8$export$e32c88dfddc6e1d8(options) {
83
82
  }).current;
84
83
  let onKeyDown = (e)=>{
85
84
  let character = $a1189052f36475e8$var$getStringForKey(e.key);
86
- if (!character || e.ctrlKey || e.metaKey) return;
85
+ if (!character || e.ctrlKey || e.metaKey || !e.currentTarget.contains(e.target)) return;
87
86
  // Do not propagate the Spacebar event if it's meant to be part of the search.
88
87
  // When we time out, the search term becomes empty, hence the check on length.
89
88
  // Trimming is to account for the case of pressing the Spacebar more than once,
@@ -156,23 +155,27 @@ function $b6837c2f80a3c32f$export$d6daf82dcd84e87c(options) {
156
155
  if (delegate.getKeyAbove) {
157
156
  var _delegate_getLastKey, _delegate_getLastKey1;
158
157
  e.preventDefault();
159
- let nextKey1 = manager.focusedKey != null ? delegate.getKeyAbove(manager.focusedKey) : (_delegate_getLastKey = delegate.getLastKey) === null || _delegate_getLastKey === void 0 ? void 0 : _delegate_getLastKey.call(delegate);
160
- if (nextKey1 == null && shouldFocusWrap) nextKey1 = (_delegate_getLastKey1 = delegate.getLastKey) === null || _delegate_getLastKey1 === void 0 ? void 0 : _delegate_getLastKey1.call(delegate, manager.focusedKey);
161
- navigateToKey(nextKey1);
158
+ let nextKey = manager.focusedKey != null ? delegate.getKeyAbove(manager.focusedKey) : (_delegate_getLastKey = delegate.getLastKey) === null || _delegate_getLastKey === void 0 ? void 0 : _delegate_getLastKey.call(delegate);
159
+ if (nextKey == null && shouldFocusWrap) nextKey = (_delegate_getLastKey1 = delegate.getLastKey) === null || _delegate_getLastKey1 === void 0 ? void 0 : _delegate_getLastKey1.call(delegate, manager.focusedKey);
160
+ navigateToKey(nextKey);
162
161
  }
163
162
  break;
164
163
  case "ArrowLeft":
165
164
  if (delegate.getKeyLeftOf) {
165
+ var _delegate_getFirstKey2, _delegate_getLastKey2;
166
166
  e.preventDefault();
167
- let nextKey2 = delegate.getKeyLeftOf(manager.focusedKey);
168
- navigateToKey(nextKey2, direction === "rtl" ? "first" : "last");
167
+ let nextKey = delegate.getKeyLeftOf(manager.focusedKey);
168
+ 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);
169
+ navigateToKey(nextKey, direction === "rtl" ? "first" : "last");
169
170
  }
170
171
  break;
171
172
  case "ArrowRight":
172
173
  if (delegate.getKeyRightOf) {
174
+ var _delegate_getLastKey3, _delegate_getFirstKey3;
173
175
  e.preventDefault();
174
- let nextKey3 = delegate.getKeyRightOf(manager.focusedKey);
175
- navigateToKey(nextKey3, direction === "rtl" ? "last" : "first");
176
+ let nextKey = delegate.getKeyRightOf(manager.focusedKey);
177
+ 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);
178
+ navigateToKey(nextKey, direction === "rtl" ? "last" : "first");
176
179
  }
177
180
  break;
178
181
  case "Home":
@@ -196,15 +199,15 @@ function $b6837c2f80a3c32f$export$d6daf82dcd84e87c(options) {
196
199
  case "PageDown":
197
200
  if (delegate.getKeyPageBelow) {
198
201
  e.preventDefault();
199
- let nextKey4 = delegate.getKeyPageBelow(manager.focusedKey);
200
- navigateToKey(nextKey4);
202
+ let nextKey = delegate.getKeyPageBelow(manager.focusedKey);
203
+ navigateToKey(nextKey);
201
204
  }
202
205
  break;
203
206
  case "PageUp":
204
207
  if (delegate.getKeyPageAbove) {
205
208
  e.preventDefault();
206
- let nextKey5 = delegate.getKeyPageAbove(manager.focusedKey);
207
- navigateToKey(nextKey5);
209
+ let nextKey = delegate.getKeyPageAbove(manager.focusedKey);
210
+ navigateToKey(nextKey);
208
211
  }
209
212
  break;
210
213
  case "a":
@@ -287,7 +290,10 @@ function $b6837c2f80a3c32f$export$d6daf82dcd84e87c(options) {
287
290
  if (element) {
288
291
  // This prevents a flash of focus on the first/last element in the collection, or the collection itself.
289
292
  (0, $glPPV$reactariautils.focusWithoutScrolling)(element);
290
- (0, $glPPV$reactariautils.scrollIntoView)(scrollRef.current, element);
293
+ let modality = (0, $glPPV$reactariainteractions.getInteractionModality)();
294
+ if (modality === "keyboard") (0, $glPPV$reactariautils.scrollIntoViewport)(element, {
295
+ containingElement: ref.current
296
+ });
291
297
  }
292
298
  }
293
299
  };
@@ -315,17 +321,21 @@ function $b6837c2f80a3c32f$export$d6daf82dcd84e87c(options) {
315
321
  }, []);
316
322
  // If not virtualized, scroll the focused element into view when the focusedKey changes.
317
323
  // When virtualized, Virtualizer handles this internally.
324
+ let lastFocusedKey = (0, $glPPV$react.useRef)(manager.focusedKey);
318
325
  (0, $glPPV$react.useEffect)(()=>{
319
326
  let modality = (0, $glPPV$reactariainteractions.getInteractionModality)();
320
- if (!isVirtualized && manager.isFocused && manager.focusedKey != null && (scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current)) {
327
+ if (manager.isFocused && manager.focusedKey != null && (scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current)) {
321
328
  let element = scrollRef.current.querySelector(`[data-key="${manager.focusedKey}"]`);
322
- if (element) {
323
- (0, $glPPV$reactariautils.scrollIntoView)(scrollRef.current, element);
324
- if (modality === "keyboard") (0, $glPPV$reactariautils.scrollIntoViewport)(element, {
329
+ if (element && modality === "keyboard") {
330
+ if (!isVirtualized) (0, $glPPV$reactariautils.scrollIntoView)(scrollRef.current, element);
331
+ (0, $glPPV$reactariautils.scrollIntoViewport)(element, {
325
332
  containingElement: ref.current
326
333
  });
327
334
  }
328
335
  }
336
+ // If the focused key becomes null (e.g. the last item is deleted), focus the whole collection.
337
+ if (manager.isFocused && manager.focusedKey == null && lastFocusedKey.current != null) (0, $glPPV$reactariafocus.focusSafely)(ref.current);
338
+ lastFocusedKey.current = manager.focusedKey;
329
339
  }, [
330
340
  isVirtualized,
331
341
  scrollRef,
@@ -379,7 +389,7 @@ function $b6837c2f80a3c32f$export$d6daf82dcd84e87c(options) {
379
389
 
380
390
 
381
391
  function $433b1145b0781e10$export$ecf600387e221c37(options) {
382
- let { selectionManager: manager , key: key , ref: ref , shouldSelectOnPressUp: shouldSelectOnPressUp , isVirtualized: isVirtualized , shouldUseVirtualFocus: shouldUseVirtualFocus , focus: focus , isDisabled: isDisabled , onAction: onAction , allowsDifferentPressOrigin: allowsDifferentPressOrigin } = options;
392
+ let { selectionManager: manager , key: key , ref: ref , shouldSelectOnPressUp: shouldSelectOnPressUp , shouldUseVirtualFocus: shouldUseVirtualFocus , focus: focus , isDisabled: isDisabled , onAction: onAction , allowsDifferentPressOrigin: allowsDifferentPressOrigin } = options;
383
393
  let onSelect = (e)=>{
384
394
  if (e.pointerType === "keyboard" && (0, $ee0bdf4faa47f2a8$export$d3e3bd3e26688c04)(e)) manager.toggleSelection(key);
385
395
  else {
@@ -485,7 +495,7 @@ function $433b1145b0781e10$export$ecf600387e221c37(options) {
485
495
  }
486
496
  };
487
497
  }
488
- if (!isVirtualized) itemProps["data-key"] = key;
498
+ itemProps["data-key"] = key;
489
499
  itemPressProps.preventFocusOnPress = shouldUseVirtualFocus;
490
500
  let { pressProps: pressProps , isPressed: isPressed } = (0, $glPPV$reactariainteractions.usePress)(itemPressProps);
491
501
  // Double clicking with a mouse with selectionBehavior = 'replace' performs an action.
@@ -559,8 +569,9 @@ function $433b1145b0781e10$var$isSelectionKey() {
559
569
  * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
560
570
  * OF ANY KIND, either express or implied. See the License for the specific language
561
571
  * governing permissions and limitations under the License.
562
- */ class $836f880b12dcae5c$export$a05409b8bb224a5a {
563
- getKeyBelow(key) {
572
+ */
573
+ class $836f880b12dcae5c$export$a05409b8bb224a5a {
574
+ getNextKey(key) {
564
575
  key = this.collection.getKeyAfter(key);
565
576
  while(key != null){
566
577
  let item = this.collection.getItem(key);
@@ -569,7 +580,7 @@ function $433b1145b0781e10$var$isSelectionKey() {
569
580
  }
570
581
  return null;
571
582
  }
572
- getKeyAbove(key) {
583
+ getPreviousKey(key) {
573
584
  key = this.collection.getKeyBefore(key);
574
585
  while(key != null){
575
586
  let item = this.collection.getItem(key);
@@ -578,6 +589,48 @@ function $433b1145b0781e10$var$isSelectionKey() {
578
589
  }
579
590
  return null;
580
591
  }
592
+ findKey(key, nextKey, shouldSkip) {
593
+ let item = this.getItem(key);
594
+ if (!item) return null;
595
+ // Find the item above or below in the same column.
596
+ let prevRect = item.getBoundingClientRect();
597
+ do {
598
+ key = nextKey(key);
599
+ item = this.getItem(key);
600
+ }while (item && shouldSkip(prevRect, item.getBoundingClientRect()));
601
+ return key;
602
+ }
603
+ isSameRow(prevRect, itemRect) {
604
+ return prevRect.top === itemRect.top || prevRect.left !== itemRect.left;
605
+ }
606
+ isSameColumn(prevRect, itemRect) {
607
+ return prevRect.left === itemRect.left || prevRect.top !== itemRect.top;
608
+ }
609
+ getKeyBelow(key) {
610
+ if (this.layout === "grid" && this.orientation === "vertical") return this.findKey(key, (key)=>this.getNextKey(key), this.isSameRow);
611
+ else return this.getNextKey(key);
612
+ }
613
+ getKeyAbove(key) {
614
+ if (this.layout === "grid" && this.orientation === "vertical") return this.findKey(key, (key)=>this.getPreviousKey(key), this.isSameRow);
615
+ else return this.getPreviousKey(key);
616
+ }
617
+ getNextColumn(key, right) {
618
+ return right ? this.getPreviousKey(key) : this.getNextKey(key);
619
+ }
620
+ getKeyRightOf(key) {
621
+ if (this.layout === "grid") {
622
+ if (this.orientation === "vertical") return this.getNextColumn(key, this.direction === "rtl");
623
+ else return this.findKey(key, (key)=>this.getNextColumn(key, this.direction === "rtl"), this.isSameColumn);
624
+ } else if (this.orientation === "horizontal") return this.getNextColumn(key, this.direction === "rtl");
625
+ return null;
626
+ }
627
+ getKeyLeftOf(key) {
628
+ if (this.layout === "grid") {
629
+ if (this.orientation === "vertical") return this.getNextColumn(key, this.direction === "ltr");
630
+ else return this.findKey(key, (key)=>this.getNextColumn(key, this.direction === "ltr"), this.isSameColumn);
631
+ } else if (this.orientation === "horizontal") return this.getNextColumn(key, this.direction === "ltr");
632
+ return null;
633
+ }
581
634
  getFirstKey() {
582
635
  let key = this.collection.getFirstKey();
583
636
  while(key != null){
@@ -603,23 +656,53 @@ function $433b1145b0781e10$var$isSelectionKey() {
603
656
  let menu = this.ref.current;
604
657
  let item = this.getItem(key);
605
658
  if (!item) return null;
606
- let pageY = Math.max(0, item.offsetTop + item.offsetHeight - menu.offsetHeight);
607
- while(item && item.offsetTop > pageY){
608
- key = this.getKeyAbove(key);
609
- item = key == null ? null : this.getItem(key);
659
+ if (!(0, $glPPV$reactariautils.isScrollable)(menu)) return this.getFirstKey();
660
+ let containerRect = menu.getBoundingClientRect();
661
+ let itemRect = item.getBoundingClientRect();
662
+ if (this.orientation === "horizontal") {
663
+ let containerX = containerRect.x - menu.scrollLeft;
664
+ let pageX = Math.max(0, itemRect.x - containerX + itemRect.width - containerRect.width);
665
+ while(item && itemRect.x - containerX > pageX){
666
+ key = this.getKeyAbove(key);
667
+ item = key == null ? null : this.getItem(key);
668
+ itemRect = item === null || item === void 0 ? void 0 : item.getBoundingClientRect();
669
+ }
670
+ } else {
671
+ let containerY = containerRect.y - menu.scrollTop;
672
+ let pageY = Math.max(0, itemRect.y - containerY + itemRect.height - containerRect.height);
673
+ while(item && itemRect.y - containerY > pageY){
674
+ key = this.getKeyAbove(key);
675
+ item = key == null ? null : this.getItem(key);
676
+ itemRect = item === null || item === void 0 ? void 0 : item.getBoundingClientRect();
677
+ }
610
678
  }
611
- return key;
679
+ return key !== null && key !== void 0 ? key : this.getFirstKey();
612
680
  }
613
681
  getKeyPageBelow(key) {
614
682
  let menu = this.ref.current;
615
683
  let item = this.getItem(key);
616
684
  if (!item) return null;
617
- let pageY = Math.min(menu.scrollHeight, item.offsetTop - item.offsetHeight + menu.offsetHeight);
618
- while(item && item.offsetTop < pageY){
619
- key = this.getKeyBelow(key);
620
- item = key == null ? null : this.getItem(key);
685
+ if (!(0, $glPPV$reactariautils.isScrollable)(menu)) return this.getLastKey();
686
+ let containerRect = menu.getBoundingClientRect();
687
+ let itemRect = item.getBoundingClientRect();
688
+ if (this.orientation === "horizontal") {
689
+ let containerX = containerRect.x - menu.scrollLeft;
690
+ let pageX = Math.min(menu.scrollWidth, itemRect.x - containerX - itemRect.width + containerRect.width);
691
+ while(item && itemRect.x - containerX < pageX){
692
+ key = this.getKeyBelow(key);
693
+ item = key == null ? null : this.getItem(key);
694
+ itemRect = item === null || item === void 0 ? void 0 : item.getBoundingClientRect();
695
+ }
696
+ } else {
697
+ let containerY = containerRect.y - menu.scrollTop;
698
+ let pageY = Math.min(menu.scrollHeight, itemRect.y - containerY - itemRect.height + containerRect.height);
699
+ while(item && itemRect.y - containerY < pageY){
700
+ key = this.getKeyBelow(key);
701
+ item = key == null ? null : this.getItem(key);
702
+ itemRect = item === null || item === void 0 ? void 0 : item.getBoundingClientRect();
703
+ }
621
704
  }
622
- return key;
705
+ return key !== null && key !== void 0 ? key : this.getLastKey();
623
706
  }
624
707
  getKeyForSearch(search, fromKey) {
625
708
  if (!this.collator) return null;
@@ -633,11 +716,29 @@ function $433b1145b0781e10$var$isSelectionKey() {
633
716
  }
634
717
  return null;
635
718
  }
636
- constructor(collection, disabledKeys, ref, collator){
637
- this.collection = collection;
638
- this.disabledKeys = disabledKeys;
639
- this.ref = ref;
640
- this.collator = collator;
719
+ constructor(...args){
720
+ if (args.length === 1) {
721
+ let opts = args[0];
722
+ this.collection = opts.collection;
723
+ this.ref = opts.ref;
724
+ this.disabledKeys = opts.disabledKeys || new Set();
725
+ this.orientation = opts.orientation;
726
+ this.direction = opts.direction;
727
+ this.layout = opts.layout || "stack";
728
+ } else {
729
+ this.collection = args[0];
730
+ this.disabledKeys = args[1];
731
+ this.ref = args[2];
732
+ this.collator = args[3];
733
+ this.layout = "stack";
734
+ this.orientation = "vertical";
735
+ }
736
+ // If this is a vertical stack, remove the left/right methods completely
737
+ // so they aren't called by useDroppableCollection.
738
+ if (this.layout === "stack" && this.orientation === "vertical") {
739
+ this.getKeyLeftOf = undefined;
740
+ this.getKeyRightOf = undefined;
741
+ }
641
742
  }
642
743
  }
643
744