@react-aria/selection 3.14.0 → 3.15.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.
@@ -10,20 +10,42 @@
10
10
  * governing permissions and limitations under the License.
11
11
  */
12
12
 
13
- import {Collection, KeyboardDelegate, Node} from '@react-types/shared';
13
+ import {Collection, Direction, KeyboardDelegate, Node, Orientation} from '@react-types/shared';
14
+ import {isScrollable} from '@react-aria/utils';
14
15
  import {Key, RefObject} from 'react';
15
16
 
17
+ interface ListKeyboardDelegateOptions<T> {
18
+ collection: Collection<Node<T>>,
19
+ ref: RefObject<HTMLElement>,
20
+ orientation?: Orientation,
21
+ direction?: Direction,
22
+ disabledKeys?: Set<Key>
23
+ }
24
+
16
25
  export class ListKeyboardDelegate<T> implements KeyboardDelegate {
17
26
  private collection: Collection<Node<T>>;
18
27
  private disabledKeys: Set<Key>;
19
28
  private ref: RefObject<HTMLElement>;
20
29
  private collator: Intl.Collator;
21
-
22
- constructor(collection: Collection<Node<T>>, disabledKeys: Set<Key>, ref: RefObject<HTMLElement>, collator?: Intl.Collator) {
23
- this.collection = collection;
24
- this.disabledKeys = disabledKeys;
25
- this.ref = ref;
26
- this.collator = collator;
30
+ private orientation?: Orientation;
31
+ private direction?: Direction;
32
+
33
+ constructor(collection: Collection<Node<T>>, disabledKeys: Set<Key>, ref: RefObject<HTMLElement>, collator?: Intl.Collator);
34
+ constructor(options: ListKeyboardDelegateOptions<T>);
35
+ constructor(...args: any[]) {
36
+ if (args.length === 1) {
37
+ let opts = args[0] as ListKeyboardDelegateOptions<T>;
38
+ this.collection = opts.collection;
39
+ this.ref = opts.ref;
40
+ this.disabledKeys = opts.disabledKeys || new Set();
41
+ this.orientation = opts.orientation;
42
+ this.direction = opts.direction;
43
+ } else {
44
+ this.collection = args[0];
45
+ this.disabledKeys = args[1];
46
+ this.ref = args[2];
47
+ this.collator = args[3];
48
+ }
27
49
  }
28
50
 
29
51
  getKeyBelow(key: Key) {
@@ -54,6 +76,22 @@ export class ListKeyboardDelegate<T> implements KeyboardDelegate {
54
76
  return null;
55
77
  }
56
78
 
79
+ getKeyRightOf(key: Key) {
80
+ if (this.orientation === 'horizontal') {
81
+ return this.direction === 'rtl' ? this.getKeyAbove(key) : this.getKeyBelow(key);
82
+ }
83
+
84
+ return null;
85
+ }
86
+
87
+ getKeyLeftOf(key: Key) {
88
+ if (this.orientation === 'horizontal') {
89
+ return this.direction === 'rtl' ? this.getKeyBelow(key) : this.getKeyAbove(key);
90
+ }
91
+
92
+ return null;
93
+ }
94
+
57
95
  getFirstKey() {
58
96
  let key = this.collection.getFirstKey();
59
97
  while (key != null) {
@@ -93,14 +131,33 @@ export class ListKeyboardDelegate<T> implements KeyboardDelegate {
93
131
  return null;
94
132
  }
95
133
 
96
- let pageY = Math.max(0, item.offsetTop + item.offsetHeight - menu.offsetHeight);
134
+ if (!isScrollable(menu)) {
135
+ return this.getFirstKey();
136
+ }
137
+
138
+ let containerRect = menu.getBoundingClientRect();
139
+ let itemRect = item.getBoundingClientRect();
140
+ if (this.orientation === 'horizontal') {
141
+ let containerX = containerRect.x - menu.scrollLeft;
142
+ let pageX = Math.max(0, (itemRect.x - containerX) + itemRect.width - containerRect.width);
97
143
 
98
- while (item && item.offsetTop > pageY) {
99
- key = this.getKeyAbove(key);
100
- item = key == null ? null : this.getItem(key);
144
+ while (item && (itemRect.x - containerX) > pageX) {
145
+ key = this.getKeyAbove(key);
146
+ item = key == null ? null : this.getItem(key);
147
+ itemRect = item?.getBoundingClientRect();
148
+ }
149
+ } else {
150
+ let containerY = containerRect.y - menu.scrollTop;
151
+ let pageY = Math.max(0, (itemRect.y - containerY) + itemRect.height - containerRect.height);
152
+
153
+ while (item && (itemRect.y - containerY) > pageY) {
154
+ key = this.getKeyAbove(key);
155
+ item = key == null ? null : this.getItem(key);
156
+ itemRect = item?.getBoundingClientRect();
157
+ }
101
158
  }
102
159
 
103
- return key;
160
+ return key ?? this.getFirstKey();
104
161
  }
105
162
 
106
163
  getKeyPageBelow(key: Key) {
@@ -110,14 +167,33 @@ export class ListKeyboardDelegate<T> implements KeyboardDelegate {
110
167
  return null;
111
168
  }
112
169
 
113
- let pageY = Math.min(menu.scrollHeight, item.offsetTop - item.offsetHeight + menu.offsetHeight);
170
+ if (!isScrollable(menu)) {
171
+ return this.getLastKey();
172
+ }
114
173
 
115
- while (item && item.offsetTop < pageY) {
116
- key = this.getKeyBelow(key);
117
- item = key == null ? null : this.getItem(key);
174
+ let containerRect = menu.getBoundingClientRect();
175
+ let itemRect = item.getBoundingClientRect();
176
+ if (this.orientation === 'horizontal') {
177
+ let containerX = containerRect.x - menu.scrollLeft;
178
+ let pageX = Math.min(menu.scrollWidth, (itemRect.x - containerX) - itemRect.width + containerRect.width);
179
+
180
+ while (item && (itemRect.x - containerX) < pageX) {
181
+ key = this.getKeyBelow(key);
182
+ item = key == null ? null : this.getItem(key);
183
+ itemRect = item?.getBoundingClientRect();
184
+ }
185
+ } else {
186
+ let containerY = containerRect.y - menu.scrollTop;
187
+ let pageY = Math.min(menu.scrollHeight, (itemRect.y - containerY) - itemRect.height + containerRect.height);
188
+
189
+ while (item && (itemRect.y - containerY) < pageY) {
190
+ key = this.getKeyBelow(key);
191
+ item = key == null ? null : this.getItem(key);
192
+ itemRect = item?.getBoundingClientRect();
193
+ }
118
194
  }
119
195
 
120
- return key;
196
+ return key ?? this.getLastKey();
121
197
  }
122
198
 
123
199
  getKeyForSearch(search: string, fromKey?: Key) {
@@ -165,6 +165,9 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
165
165
  if (delegate.getKeyLeftOf) {
166
166
  e.preventDefault();
167
167
  let nextKey = delegate.getKeyLeftOf(manager.focusedKey);
168
+ if (nextKey == null && shouldFocusWrap) {
169
+ nextKey = direction === 'rtl' ? delegate.getFirstKey?.(manager.focusedKey) : delegate.getLastKey?.(manager.focusedKey);
170
+ }
168
171
  navigateToKey(nextKey, direction === 'rtl' ? 'first' : 'last');
169
172
  }
170
173
  break;
@@ -173,6 +176,9 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
173
176
  if (delegate.getKeyRightOf) {
174
177
  e.preventDefault();
175
178
  let nextKey = delegate.getKeyRightOf(manager.focusedKey);
179
+ if (nextKey == null && shouldFocusWrap) {
180
+ nextKey = direction === 'rtl' ? delegate.getLastKey?.(manager.focusedKey) : delegate.getFirstKey?.(manager.focusedKey);
181
+ }
176
182
  navigateToKey(nextKey, direction === 'rtl' ? 'last' : 'first');
177
183
  }
178
184
  break;
@@ -53,7 +53,7 @@ export function useTypeSelect(options: AriaTypeSelectOptions): TypeSelectAria {
53
53
 
54
54
  let onKeyDown = (e: KeyboardEvent) => {
55
55
  let character = getStringForKey(e.key);
56
- if (!character || e.ctrlKey || e.metaKey) {
56
+ if (!character || e.ctrlKey || e.metaKey || !e.currentTarget.contains(e.target as HTMLElement)) {
57
57
  return;
58
58
  }
59
59