@react-aria/landmark 3.0.0-nightly.3751 → 3.0.0-nightly.3760
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 +27 -20
- package/dist/main.js +27 -20
- package/dist/main.js.map +1 -1
- package/dist/module.js +27 -20
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/useLandmark.ts +46 -39
package/dist/import.mjs
CHANGED
|
@@ -79,8 +79,8 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
79
79
|
this.isListening = false;
|
|
80
80
|
}
|
|
81
81
|
focusLandmark(landmark, direction) {
|
|
82
|
-
var _this_landmarks_find;
|
|
83
|
-
(_this_landmarks_find = this.landmarks.find((l)=>l.ref.current === landmark)) === null || _this_landmarks_find === void 0 ? void 0 : _this_landmarks_find.focus(direction);
|
|
82
|
+
var _this_landmarks_find, _this_landmarks_find_focus;
|
|
83
|
+
(_this_landmarks_find = this.landmarks.find((l)=>l.ref.current === landmark)) === null || _this_landmarks_find === void 0 ? void 0 : (_this_landmarks_find_focus = _this_landmarks_find.focus) === null || _this_landmarks_find_focus === void 0 ? void 0 : _this_landmarks_find_focus.call(_this_landmarks_find, direction);
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
86
86
|
* Return set of landmarks with a specific role.
|
|
@@ -94,7 +94,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
94
94
|
}
|
|
95
95
|
addLandmark(newLandmark) {
|
|
96
96
|
this.setupIfNeeded();
|
|
97
|
-
if (this.landmarks.find((landmark)=>landmark.ref === newLandmark.ref)) return;
|
|
97
|
+
if (this.landmarks.find((landmark)=>landmark.ref === newLandmark.ref) || !newLandmark.ref.current) return;
|
|
98
98
|
if (this.landmarks.filter((landmark)=>landmark.role === "main").length > 1) console.error('Page can contain no more than one landmark with the role "main".');
|
|
99
99
|
if (this.landmarks.length === 0) {
|
|
100
100
|
this.landmarks = [
|
|
@@ -165,7 +165,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
165
165
|
l
|
|
166
166
|
]));
|
|
167
167
|
let currentElement = element;
|
|
168
|
-
while(currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body)currentElement = currentElement.parentElement;
|
|
168
|
+
while(currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body && currentElement.parentElement)currentElement = currentElement.parentElement;
|
|
169
169
|
return landmarkMap.get(currentElement);
|
|
170
170
|
}
|
|
171
171
|
/**
|
|
@@ -174,6 +174,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
174
174
|
* If not inside a landmark, will return first landmark.
|
|
175
175
|
* Returns undefined if there are no landmarks.
|
|
176
176
|
*/ getNextLandmark(element, { backward: backward }) {
|
|
177
|
+
var _this_landmarks_nextLandmarkIndex_ref_current;
|
|
177
178
|
let currentLandmark = this.closestLandmark(element);
|
|
178
179
|
let nextLandmarkIndex = backward ? this.landmarks.length - 1 : 0;
|
|
179
180
|
if (currentLandmark) nextLandmarkIndex = this.landmarks.indexOf(currentLandmark) + (backward ? -1 : 1);
|
|
@@ -205,7 +206,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
205
206
|
if (wrapIfNeeded()) return undefined;
|
|
206
207
|
// Skip over hidden landmarks.
|
|
207
208
|
let i = nextLandmarkIndex;
|
|
208
|
-
while(this.landmarks[nextLandmarkIndex].ref.current.closest("[aria-hidden=true]")){
|
|
209
|
+
while((_this_landmarks_nextLandmarkIndex_ref_current = this.landmarks[nextLandmarkIndex].ref.current) === null || _this_landmarks_nextLandmarkIndex_ref_current === void 0 ? void 0 : _this_landmarks_nextLandmarkIndex_ref_current.closest("[aria-hidden=true]")){
|
|
209
210
|
nextLandmarkIndex += backward ? -1 : 1;
|
|
210
211
|
if (wrapIfNeeded()) return undefined;
|
|
211
212
|
if (nextLandmarkIndex === i) break;
|
|
@@ -228,7 +229,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
228
229
|
}
|
|
229
230
|
focusMain() {
|
|
230
231
|
let main = this.getLandmarkByRole("main");
|
|
231
|
-
if (main && document.contains(main.ref.current)) {
|
|
232
|
+
if (main && main.ref.current && document.contains(main.ref.current)) {
|
|
232
233
|
this.focusLandmark(main.ref.current, "forward");
|
|
233
234
|
return true;
|
|
234
235
|
}
|
|
@@ -248,7 +249,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
// Otherwise, focus the landmark itself
|
|
251
|
-
if (document.contains(nextLandmark.ref.current)) {
|
|
252
|
+
if (nextLandmark.ref.current && document.contains(nextLandmark.ref.current)) {
|
|
252
253
|
this.focusLandmark(nextLandmark.ref.current, backward ? "backward" : "forward");
|
|
253
254
|
return true;
|
|
254
255
|
}
|
|
@@ -287,21 +288,26 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
287
288
|
instance.setupIfNeeded();
|
|
288
289
|
return {
|
|
289
290
|
navigate (direction, opts) {
|
|
290
|
-
|
|
291
|
+
let element = (opts === null || opts === void 0 ? void 0 : opts.from) || document.activeElement;
|
|
292
|
+
return instance.navigate(element, direction === "backward");
|
|
291
293
|
},
|
|
292
294
|
focusNext (opts) {
|
|
293
|
-
|
|
295
|
+
let element = (opts === null || opts === void 0 ? void 0 : opts.from) || document.activeElement;
|
|
296
|
+
return instance.navigate(element, false);
|
|
294
297
|
},
|
|
295
298
|
focusPrevious (opts) {
|
|
296
|
-
|
|
299
|
+
let element = (opts === null || opts === void 0 ? void 0 : opts.from) || document.activeElement;
|
|
300
|
+
return instance.navigate(element, true);
|
|
297
301
|
},
|
|
298
302
|
focusMain () {
|
|
299
303
|
return instance.focusMain();
|
|
300
304
|
},
|
|
301
305
|
dispose () {
|
|
302
|
-
instance
|
|
303
|
-
|
|
304
|
-
|
|
306
|
+
if (instance) {
|
|
307
|
+
instance.refCount--;
|
|
308
|
+
instance.teardownIfNeeded();
|
|
309
|
+
instance = null;
|
|
310
|
+
}
|
|
305
311
|
}
|
|
306
312
|
};
|
|
307
313
|
}
|
|
@@ -323,13 +329,13 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
323
329
|
function $a86207c5d7f7e1fb$export$f50151dbd51cd1d9() {
|
|
324
330
|
// Get the current landmark manager and create a controller using it.
|
|
325
331
|
let instance = $a86207c5d7f7e1fb$var$getLandmarkManager();
|
|
326
|
-
let controller = instance.createLandmarkController();
|
|
332
|
+
let controller = instance === null || instance === void 0 ? void 0 : instance.createLandmarkController();
|
|
327
333
|
let unsubscribe = $a86207c5d7f7e1fb$var$subscribe(()=>{
|
|
328
334
|
// If the landmark manager changes, dispose the old
|
|
329
335
|
// controller and create a new one.
|
|
330
|
-
controller.dispose();
|
|
336
|
+
controller === null || controller === void 0 ? void 0 : controller.dispose();
|
|
331
337
|
instance = $a86207c5d7f7e1fb$var$getLandmarkManager();
|
|
332
|
-
controller = instance.createLandmarkController();
|
|
338
|
+
controller = instance === null || instance === void 0 ? void 0 : instance.createLandmarkController();
|
|
333
339
|
});
|
|
334
340
|
// Return a wrapper that proxies requests to the current controller instance.
|
|
335
341
|
return {
|
|
@@ -346,9 +352,9 @@ function $a86207c5d7f7e1fb$export$f50151dbd51cd1d9() {
|
|
|
346
352
|
return controller.focusMain();
|
|
347
353
|
},
|
|
348
354
|
dispose () {
|
|
349
|
-
controller.dispose();
|
|
355
|
+
controller === null || controller === void 0 ? void 0 : controller.dispose();
|
|
350
356
|
unsubscribe();
|
|
351
|
-
controller =
|
|
357
|
+
controller = undefined;
|
|
352
358
|
instance = null;
|
|
353
359
|
}
|
|
354
360
|
};
|
|
@@ -369,7 +375,7 @@ function $a86207c5d7f7e1fb$export$4cc632584fd87fae(props, ref) {
|
|
|
369
375
|
setIsLandmarkFocused
|
|
370
376
|
]);
|
|
371
377
|
(0, $TvsbU$useLayoutEffect)(()=>{
|
|
372
|
-
return manager.registerLandmark({
|
|
378
|
+
if (manager) return manager.registerLandmark({
|
|
373
379
|
ref: ref,
|
|
374
380
|
label: label,
|
|
375
381
|
role: role,
|
|
@@ -386,7 +392,8 @@ function $a86207c5d7f7e1fb$export$4cc632584fd87fae(props, ref) {
|
|
|
386
392
|
blur
|
|
387
393
|
]);
|
|
388
394
|
(0, $TvsbU$useEffect)(()=>{
|
|
389
|
-
|
|
395
|
+
var _ref_current;
|
|
396
|
+
if (isLandmarkFocused) (_ref_current = ref.current) === null || _ref_current === void 0 ? void 0 : _ref_current.focus();
|
|
390
397
|
}, [
|
|
391
398
|
isLandmarkFocused,
|
|
392
399
|
ref
|
package/dist/main.js
CHANGED
|
@@ -88,8 +88,8 @@ class $202c109aedff6705$var$LandmarkManager {
|
|
|
88
88
|
this.isListening = false;
|
|
89
89
|
}
|
|
90
90
|
focusLandmark(landmark, direction) {
|
|
91
|
-
var _this_landmarks_find;
|
|
92
|
-
(_this_landmarks_find = this.landmarks.find((l)=>l.ref.current === landmark)) === null || _this_landmarks_find === void 0 ? void 0 : _this_landmarks_find.focus(direction);
|
|
91
|
+
var _this_landmarks_find, _this_landmarks_find_focus;
|
|
92
|
+
(_this_landmarks_find = this.landmarks.find((l)=>l.ref.current === landmark)) === null || _this_landmarks_find === void 0 ? void 0 : (_this_landmarks_find_focus = _this_landmarks_find.focus) === null || _this_landmarks_find_focus === void 0 ? void 0 : _this_landmarks_find_focus.call(_this_landmarks_find, direction);
|
|
93
93
|
}
|
|
94
94
|
/**
|
|
95
95
|
* Return set of landmarks with a specific role.
|
|
@@ -103,7 +103,7 @@ class $202c109aedff6705$var$LandmarkManager {
|
|
|
103
103
|
}
|
|
104
104
|
addLandmark(newLandmark) {
|
|
105
105
|
this.setupIfNeeded();
|
|
106
|
-
if (this.landmarks.find((landmark)=>landmark.ref === newLandmark.ref)) return;
|
|
106
|
+
if (this.landmarks.find((landmark)=>landmark.ref === newLandmark.ref) || !newLandmark.ref.current) return;
|
|
107
107
|
if (this.landmarks.filter((landmark)=>landmark.role === "main").length > 1) console.error('Page can contain no more than one landmark with the role "main".');
|
|
108
108
|
if (this.landmarks.length === 0) {
|
|
109
109
|
this.landmarks = [
|
|
@@ -174,7 +174,7 @@ class $202c109aedff6705$var$LandmarkManager {
|
|
|
174
174
|
l
|
|
175
175
|
]));
|
|
176
176
|
let currentElement = element;
|
|
177
|
-
while(currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body)currentElement = currentElement.parentElement;
|
|
177
|
+
while(currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body && currentElement.parentElement)currentElement = currentElement.parentElement;
|
|
178
178
|
return landmarkMap.get(currentElement);
|
|
179
179
|
}
|
|
180
180
|
/**
|
|
@@ -183,6 +183,7 @@ class $202c109aedff6705$var$LandmarkManager {
|
|
|
183
183
|
* If not inside a landmark, will return first landmark.
|
|
184
184
|
* Returns undefined if there are no landmarks.
|
|
185
185
|
*/ getNextLandmark(element, { backward: backward }) {
|
|
186
|
+
var _this_landmarks_nextLandmarkIndex_ref_current;
|
|
186
187
|
let currentLandmark = this.closestLandmark(element);
|
|
187
188
|
let nextLandmarkIndex = backward ? this.landmarks.length - 1 : 0;
|
|
188
189
|
if (currentLandmark) nextLandmarkIndex = this.landmarks.indexOf(currentLandmark) + (backward ? -1 : 1);
|
|
@@ -214,7 +215,7 @@ class $202c109aedff6705$var$LandmarkManager {
|
|
|
214
215
|
if (wrapIfNeeded()) return undefined;
|
|
215
216
|
// Skip over hidden landmarks.
|
|
216
217
|
let i = nextLandmarkIndex;
|
|
217
|
-
while(this.landmarks[nextLandmarkIndex].ref.current.closest("[aria-hidden=true]")){
|
|
218
|
+
while((_this_landmarks_nextLandmarkIndex_ref_current = this.landmarks[nextLandmarkIndex].ref.current) === null || _this_landmarks_nextLandmarkIndex_ref_current === void 0 ? void 0 : _this_landmarks_nextLandmarkIndex_ref_current.closest("[aria-hidden=true]")){
|
|
218
219
|
nextLandmarkIndex += backward ? -1 : 1;
|
|
219
220
|
if (wrapIfNeeded()) return undefined;
|
|
220
221
|
if (nextLandmarkIndex === i) break;
|
|
@@ -237,7 +238,7 @@ class $202c109aedff6705$var$LandmarkManager {
|
|
|
237
238
|
}
|
|
238
239
|
focusMain() {
|
|
239
240
|
let main = this.getLandmarkByRole("main");
|
|
240
|
-
if (main && document.contains(main.ref.current)) {
|
|
241
|
+
if (main && main.ref.current && document.contains(main.ref.current)) {
|
|
241
242
|
this.focusLandmark(main.ref.current, "forward");
|
|
242
243
|
return true;
|
|
243
244
|
}
|
|
@@ -257,7 +258,7 @@ class $202c109aedff6705$var$LandmarkManager {
|
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
260
|
// Otherwise, focus the landmark itself
|
|
260
|
-
if (document.contains(nextLandmark.ref.current)) {
|
|
261
|
+
if (nextLandmark.ref.current && document.contains(nextLandmark.ref.current)) {
|
|
261
262
|
this.focusLandmark(nextLandmark.ref.current, backward ? "backward" : "forward");
|
|
262
263
|
return true;
|
|
263
264
|
}
|
|
@@ -296,21 +297,26 @@ class $202c109aedff6705$var$LandmarkManager {
|
|
|
296
297
|
instance.setupIfNeeded();
|
|
297
298
|
return {
|
|
298
299
|
navigate (direction, opts) {
|
|
299
|
-
|
|
300
|
+
let element = (opts === null || opts === void 0 ? void 0 : opts.from) || document.activeElement;
|
|
301
|
+
return instance.navigate(element, direction === "backward");
|
|
300
302
|
},
|
|
301
303
|
focusNext (opts) {
|
|
302
|
-
|
|
304
|
+
let element = (opts === null || opts === void 0 ? void 0 : opts.from) || document.activeElement;
|
|
305
|
+
return instance.navigate(element, false);
|
|
303
306
|
},
|
|
304
307
|
focusPrevious (opts) {
|
|
305
|
-
|
|
308
|
+
let element = (opts === null || opts === void 0 ? void 0 : opts.from) || document.activeElement;
|
|
309
|
+
return instance.navigate(element, true);
|
|
306
310
|
},
|
|
307
311
|
focusMain () {
|
|
308
312
|
return instance.focusMain();
|
|
309
313
|
},
|
|
310
314
|
dispose () {
|
|
311
|
-
instance
|
|
312
|
-
|
|
313
|
-
|
|
315
|
+
if (instance) {
|
|
316
|
+
instance.refCount--;
|
|
317
|
+
instance.teardownIfNeeded();
|
|
318
|
+
instance = null;
|
|
319
|
+
}
|
|
314
320
|
}
|
|
315
321
|
};
|
|
316
322
|
}
|
|
@@ -332,13 +338,13 @@ class $202c109aedff6705$var$LandmarkManager {
|
|
|
332
338
|
function $202c109aedff6705$export$f50151dbd51cd1d9() {
|
|
333
339
|
// Get the current landmark manager and create a controller using it.
|
|
334
340
|
let instance = $202c109aedff6705$var$getLandmarkManager();
|
|
335
|
-
let controller = instance.createLandmarkController();
|
|
341
|
+
let controller = instance === null || instance === void 0 ? void 0 : instance.createLandmarkController();
|
|
336
342
|
let unsubscribe = $202c109aedff6705$var$subscribe(()=>{
|
|
337
343
|
// If the landmark manager changes, dispose the old
|
|
338
344
|
// controller and create a new one.
|
|
339
|
-
controller.dispose();
|
|
345
|
+
controller === null || controller === void 0 ? void 0 : controller.dispose();
|
|
340
346
|
instance = $202c109aedff6705$var$getLandmarkManager();
|
|
341
|
-
controller = instance.createLandmarkController();
|
|
347
|
+
controller = instance === null || instance === void 0 ? void 0 : instance.createLandmarkController();
|
|
342
348
|
});
|
|
343
349
|
// Return a wrapper that proxies requests to the current controller instance.
|
|
344
350
|
return {
|
|
@@ -355,9 +361,9 @@ function $202c109aedff6705$export$f50151dbd51cd1d9() {
|
|
|
355
361
|
return controller.focusMain();
|
|
356
362
|
},
|
|
357
363
|
dispose () {
|
|
358
|
-
controller.dispose();
|
|
364
|
+
controller === null || controller === void 0 ? void 0 : controller.dispose();
|
|
359
365
|
unsubscribe();
|
|
360
|
-
controller =
|
|
366
|
+
controller = undefined;
|
|
361
367
|
instance = null;
|
|
362
368
|
}
|
|
363
369
|
};
|
|
@@ -378,7 +384,7 @@ function $202c109aedff6705$export$4cc632584fd87fae(props, ref) {
|
|
|
378
384
|
setIsLandmarkFocused
|
|
379
385
|
]);
|
|
380
386
|
(0, $8Ore6$reactariautils.useLayoutEffect)(()=>{
|
|
381
|
-
return manager.registerLandmark({
|
|
387
|
+
if (manager) return manager.registerLandmark({
|
|
382
388
|
ref: ref,
|
|
383
389
|
label: label,
|
|
384
390
|
role: role,
|
|
@@ -395,7 +401,8 @@ function $202c109aedff6705$export$4cc632584fd87fae(props, ref) {
|
|
|
395
401
|
blur
|
|
396
402
|
]);
|
|
397
403
|
(0, $8Ore6$react.useEffect)(()=>{
|
|
398
|
-
|
|
404
|
+
var _ref_current;
|
|
405
|
+
if (isLandmarkFocused) (_ref_current = ref.current) === null || _ref_current === void 0 ? void 0 : _ref_current.focus();
|
|
399
406
|
}, [
|
|
400
407
|
isLandmarkFocused,
|
|
401
408
|
ref
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","sources":["packages/@react-aria/landmark/src/index.ts","packages/@react-aria/landmark/src/useLandmark.ts"],"sourcesContent":["/*\n * Copyright 2022 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport type {AriaLandmarkRole, AriaLandmarkProps, LandmarkAria, LandmarkController} from './useLandmark';\nexport {useLandmark, createLandmarkController} from './useLandmark';\n","/*\n * Copyright 2022 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {AriaLabelingProps, DOMAttributes, FocusableElement} from '@react-types/shared';\nimport {MutableRefObject, useCallback, useEffect, useState} from 'react';\nimport {useLayoutEffect} from '@react-aria/utils';\nimport {useSyncExternalStore} from 'use-sync-external-store/shim/index.js';\n\nexport type AriaLandmarkRole = 'main' | 'region' | 'search' | 'navigation' | 'form' | 'banner' | 'contentinfo' | 'complementary';\n\nexport interface AriaLandmarkProps extends AriaLabelingProps {\n role: AriaLandmarkRole,\n focus?: (direction: 'forward' | 'backward') => void\n}\n\nexport interface LandmarkAria {\n landmarkProps: DOMAttributes\n}\n\n// Increment this version number whenever the\n// LandmarkManagerApi or Landmark interfaces change.\nconst LANDMARK_API_VERSION = 1;\n\n// Minimal API for LandmarkManager that must continue to work between versions.\n// Changes to this interface are considered breaking. New methods/properties are\n// safe to add, but changes or removals are not allowed (same as public APIs).\ninterface LandmarkManagerApi {\n version: number,\n createLandmarkController(): LandmarkController,\n registerLandmark(landmark: Landmark): () => void\n}\n\n// Changes to this interface are considered breaking.\n// New properties MUST be optional so that registering a landmark\n// from an older version of useLandmark against a newer version of\n// LandmarkManager does not crash.\ninterface Landmark {\n ref: MutableRefObject<Element>,\n role: AriaLandmarkRole,\n label?: string,\n lastFocused?: FocusableElement,\n focus: (direction: 'forward' | 'backward') => void,\n blur: () => void\n}\n\nexport interface LandmarkControllerOptions {\n /**\n * The element from which to start navigating.\n * @default document.activeElement\n */\n from?: Element\n}\n\n/** A LandmarkController allows programmatic navigation of landmarks. */\nexport interface LandmarkController {\n /** Moves focus to the next landmark. */\n focusNext(opts?: LandmarkControllerOptions): boolean,\n /** Moves focus to the previous landmark. */\n focusPrevious(opts?: LandmarkControllerOptions): boolean,\n /** Moves focus to the main landmark. */\n focusMain(): boolean,\n /** Moves focus either forward or backward in the landmark sequence. */\n navigate(direction: 'forward' | 'backward', opts?: LandmarkControllerOptions): boolean,\n /**\n * Disposes the landmark controller. When no landmarks are registered, and no\n * controllers are active, the landmark keyboard listeners are removed from the page.\n */\n dispose(): void\n}\n\n// Symbol under which the singleton landmark manager instance is attached to the document.\nconst landmarkSymbol = Symbol.for('react-aria-landmark-manager');\n\nfunction subscribe(fn: () => void) {\n document.addEventListener('react-aria-landmark-manager-change', fn);\n return () => document.removeEventListener('react-aria-landmark-manager-change', fn);\n}\n\nfunction getLandmarkManager(): LandmarkManagerApi | null {\n if (typeof document === 'undefined') {\n return null;\n }\n\n // Reuse an existing instance if it has the same or greater version.\n let instance = document[landmarkSymbol];\n if (instance && instance.version >= LANDMARK_API_VERSION) {\n return instance;\n }\n\n // Otherwise, create a new instance and dispatch an event so anything using the existing\n // instance updates and re-registers their landmarks with the new one.\n document[landmarkSymbol] = new LandmarkManager();\n document.dispatchEvent(new CustomEvent('react-aria-landmark-manager-change'));\n return document[landmarkSymbol];\n}\n\n// Subscribes a React component to the current landmark manager instance.\nfunction useLandmarkManager(): LandmarkManagerApi | null {\n return useSyncExternalStore(subscribe, getLandmarkManager, getLandmarkManager);\n}\n\nclass LandmarkManager implements LandmarkManagerApi {\n private landmarks: Array<Landmark> = [];\n private isListening = false;\n private refCount = 0;\n public version = LANDMARK_API_VERSION;\n\n constructor() {\n this.f6Handler = this.f6Handler.bind(this);\n this.focusinHandler = this.focusinHandler.bind(this);\n this.focusoutHandler = this.focusoutHandler.bind(this);\n }\n\n private setupIfNeeded() {\n if (this.isListening) {\n return;\n }\n document.addEventListener('keydown', this.f6Handler, {capture: true});\n document.addEventListener('focusin', this.focusinHandler, {capture: true});\n document.addEventListener('focusout', this.focusoutHandler, {capture: true});\n this.isListening = true;\n }\n\n private teardownIfNeeded() {\n if (!this.isListening || this.landmarks.length > 0 || this.refCount > 0) {\n return;\n }\n document.removeEventListener('keydown', this.f6Handler, {capture: true});\n document.removeEventListener('focusin', this.focusinHandler, {capture: true});\n document.removeEventListener('focusout', this.focusoutHandler, {capture: true});\n this.isListening = false;\n }\n\n private focusLandmark(landmark: Element, direction: 'forward' | 'backward') {\n this.landmarks.find(l => l.ref.current === landmark)?.focus(direction);\n }\n\n /**\n * Return set of landmarks with a specific role.\n */\n private getLandmarksByRole(role: AriaLandmarkRole) {\n return new Set(this.landmarks.filter(l => l.role === role));\n }\n\n /**\n * Return first landmark with a specific role.\n */\n private getLandmarkByRole(role: AriaLandmarkRole) {\n return this.landmarks.find(l => l.role === role);\n }\n\n private addLandmark(newLandmark: Landmark) {\n this.setupIfNeeded();\n if (this.landmarks.find(landmark => landmark.ref === newLandmark.ref)) {\n return;\n }\n\n if (this.landmarks.filter(landmark => landmark.role === 'main').length > 1) {\n console.error('Page can contain no more than one landmark with the role \"main\".');\n }\n\n if (this.landmarks.length === 0) {\n this.landmarks = [newLandmark];\n this.checkLabels(newLandmark.role);\n return;\n }\n\n\n // Binary search to insert new landmark based on position in document relative to existing landmarks.\n // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition\n let start = 0;\n let end = this.landmarks.length - 1;\n while (start <= end) {\n let mid = Math.floor((start + end) / 2);\n let comparedPosition = newLandmark.ref.current.compareDocumentPosition(this.landmarks[mid].ref.current as Node);\n let isNewAfterExisting = Boolean((comparedPosition & Node.DOCUMENT_POSITION_PRECEDING) || (comparedPosition & Node.DOCUMENT_POSITION_CONTAINS));\n\n if (isNewAfterExisting) {\n start = mid + 1;\n } else {\n end = mid - 1;\n }\n }\n\n this.landmarks.splice(start, 0, newLandmark);\n this.checkLabels(newLandmark.role);\n }\n\n private updateLandmark(landmark: Pick<Landmark, 'ref'> & Partial<Landmark>) {\n let index = this.landmarks.findIndex(l => l.ref === landmark.ref);\n if (index >= 0) {\n this.landmarks[index] = {...this.landmarks[index], ...landmark};\n this.checkLabels(this.landmarks[index].role);\n }\n }\n\n private removeLandmark(ref: MutableRefObject<Element>) {\n this.landmarks = this.landmarks.filter(landmark => landmark.ref !== ref);\n this.teardownIfNeeded();\n }\n\n /**\n * Warn if there are 2+ landmarks with the same role but no label.\n * Labels for landmarks with the same role must also be unique.\n *\n * See https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/.\n */\n private checkLabels(role: AriaLandmarkRole) {\n let landmarksWithRole = this.getLandmarksByRole(role);\n if (landmarksWithRole.size > 1) {\n let duplicatesWithoutLabel = [...landmarksWithRole].filter(landmark => !landmark.label);\n if (duplicatesWithoutLabel.length > 0) {\n console.warn(\n `Page contains more than one landmark with the '${role}' role. If two or more landmarks on a page share the same role, all must be labeled with an aria-label or aria-labelledby attribute: `,\n duplicatesWithoutLabel.map(landmark => landmark.ref.current)\n );\n } else {\n let labels = [...landmarksWithRole].map(landmark => landmark.label);\n let duplicateLabels = labels.filter((item, index) => labels.indexOf(item) !== index);\n\n duplicateLabels.forEach((label) => {\n console.warn(\n `Page contains more than one landmark with the '${role}' role and '${label}' label. If two or more landmarks on a page share the same role, they must have unique labels: `,\n [...landmarksWithRole].filter(landmark => landmark.label === label).map(landmark => landmark.ref.current)\n );\n });\n }\n }\n }\n\n /**\n * Get the landmark that is the closest parent in the DOM.\n * Returns undefined if no parent is a landmark.\n */\n private closestLandmark(element: Element) {\n let landmarkMap = new Map(this.landmarks.map(l => [l.ref.current, l]));\n let currentElement = element;\n while (currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body) {\n currentElement = currentElement.parentElement;\n }\n return landmarkMap.get(currentElement);\n }\n\n /**\n * Gets the next landmark, in DOM focus order, or previous if backwards is specified.\n * If last landmark, next should be the first landmark.\n * If not inside a landmark, will return first landmark.\n * Returns undefined if there are no landmarks.\n */\n private getNextLandmark(element: Element, {backward}: {backward?: boolean }) {\n let currentLandmark = this.closestLandmark(element);\n let nextLandmarkIndex = backward ? this.landmarks.length - 1 : 0;\n if (currentLandmark) {\n nextLandmarkIndex = this.landmarks.indexOf(currentLandmark) + (backward ? -1 : 1);\n }\n\n let wrapIfNeeded = () => {\n // When we reach the end of the landmark sequence, fire a custom event that can be listened for by applications.\n // If this event is canceled, we return immediately. This can be used to implement landmark navigation across iframes.\n if (nextLandmarkIndex < 0) {\n if (!element.dispatchEvent(new CustomEvent('react-aria-landmark-navigation', {detail: {direction: 'backward'}, bubbles: true, cancelable: true}))) {\n return true;\n }\n\n nextLandmarkIndex = this.landmarks.length - 1;\n } else if (nextLandmarkIndex >= this.landmarks.length) {\n if (!element.dispatchEvent(new CustomEvent('react-aria-landmark-navigation', {detail: {direction: 'forward'}, bubbles: true, cancelable: true}))) {\n return true;\n }\n\n nextLandmarkIndex = 0;\n }\n\n if (nextLandmarkIndex < 0 || nextLandmarkIndex >= this.landmarks.length) {\n return true;\n }\n\n return false;\n };\n\n if (wrapIfNeeded()) {\n return undefined;\n }\n\n // Skip over hidden landmarks.\n let i = nextLandmarkIndex;\n while (this.landmarks[nextLandmarkIndex].ref.current.closest('[aria-hidden=true]')) {\n nextLandmarkIndex += backward ? -1 : 1;\n if (wrapIfNeeded()) {\n return undefined;\n }\n\n if (nextLandmarkIndex === i) {\n break;\n }\n }\n\n return this.landmarks[nextLandmarkIndex];\n }\n\n /**\n * Look at next landmark. If an element was previously focused inside, restore focus there.\n * If not, focus the landmark itself.\n * If no landmarks at all, or none with focusable elements, don't move focus.\n */\n private f6Handler(e: KeyboardEvent) {\n if (e.key === 'F6') {\n // If alt key pressed, focus main landmark, otherwise navigate forward or backward based on shift key.\n let handled = e.altKey ? this.focusMain() : this.navigate(e.target as Element, e.shiftKey);\n if (handled) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n }\n\n private focusMain() {\n let main = this.getLandmarkByRole('main');\n if (main && document.contains(main.ref.current)) {\n this.focusLandmark(main.ref.current, 'forward');\n return true;\n }\n\n return false;\n }\n\n private navigate(from: Element, backward: boolean) {\n let nextLandmark = this.getNextLandmark(from, {\n backward\n });\n\n if (!nextLandmark) {\n return false;\n }\n\n // If something was previously focused in the next landmark, then return focus to it\n if (nextLandmark.lastFocused) {\n let lastFocused = nextLandmark.lastFocused;\n if (document.body.contains(lastFocused)) {\n lastFocused.focus();\n return true;\n }\n }\n\n // Otherwise, focus the landmark itself\n if (document.contains(nextLandmark.ref.current)) {\n this.focusLandmark(nextLandmark.ref.current, backward ? 'backward' : 'forward');\n return true;\n }\n\n return false;\n }\n\n /**\n * Sets lastFocused for a landmark, if focus is moved within that landmark.\n * Lets the last focused landmark know it was blurred if something else is focused.\n */\n private focusinHandler(e: FocusEvent) {\n let currentLandmark = this.closestLandmark(e.target as Element);\n if (currentLandmark && currentLandmark.ref.current !== e.target) {\n this.updateLandmark({ref: currentLandmark.ref, lastFocused: e.target as FocusableElement});\n }\n let previousFocusedElement = e.relatedTarget as Element;\n if (previousFocusedElement) {\n let closestPreviousLandmark = this.closestLandmark(previousFocusedElement);\n if (closestPreviousLandmark && closestPreviousLandmark.ref.current === previousFocusedElement) {\n closestPreviousLandmark.blur();\n }\n }\n }\n\n /**\n * Track if the focus is lost to the body. If it is, do cleanup on the landmark that last had focus.\n */\n private focusoutHandler(e: FocusEvent) {\n let previousFocusedElement = e.target as Element;\n let nextFocusedElement = e.relatedTarget;\n // the === document seems to be a jest thing for focus to go there on generic blur event such as landmark.blur();\n // browsers appear to send focus instead to document.body and the relatedTarget is null when that happens\n if (!nextFocusedElement || nextFocusedElement === document) {\n let closestPreviousLandmark = this.closestLandmark(previousFocusedElement);\n if (closestPreviousLandmark && closestPreviousLandmark.ref.current === previousFocusedElement) {\n closestPreviousLandmark.blur();\n }\n }\n }\n\n public createLandmarkController(): LandmarkController {\n let instance = this;\n instance.refCount++;\n instance.setupIfNeeded();\n return {\n navigate(direction, opts) {\n return instance.navigate(opts?.from || document.activeElement, direction === 'backward');\n },\n focusNext(opts) {\n return instance.navigate(opts?.from || document.activeElement, false);\n },\n focusPrevious(opts) {\n return instance.navigate(opts?.from || document.activeElement, true);\n },\n focusMain() {\n return instance.focusMain();\n },\n dispose() {\n instance.refCount--;\n instance.teardownIfNeeded();\n instance = null;\n }\n };\n }\n\n public registerLandmark(landmark: Landmark): () => void {\n if (this.landmarks.find(l => l.ref === landmark.ref)) {\n this.updateLandmark(landmark);\n } else {\n this.addLandmark(landmark);\n }\n\n return () => this.removeLandmark(landmark.ref);\n }\n}\n\n/** Creates a LandmarkController, which allows programmatic navigation of landmarks. */\nexport function createLandmarkController(): LandmarkController {\n // Get the current landmark manager and create a controller using it.\n let instance = getLandmarkManager();\n let controller = instance.createLandmarkController();\n\n let unsubscribe = subscribe(() => {\n // If the landmark manager changes, dispose the old\n // controller and create a new one.\n controller.dispose();\n instance = getLandmarkManager();\n controller = instance.createLandmarkController();\n });\n\n // Return a wrapper that proxies requests to the current controller instance.\n return {\n navigate(direction, opts) {\n return controller.navigate(direction, opts);\n },\n focusNext(opts) {\n return controller.focusNext(opts);\n },\n focusPrevious(opts) {\n return controller.focusPrevious(opts);\n },\n focusMain() {\n return controller.focusMain();\n },\n dispose() {\n controller.dispose();\n unsubscribe();\n controller = null;\n instance = null;\n }\n };\n}\n\n/**\n * Provides landmark navigation in an application. Call this with a role and label to register a landmark navigable with F6.\n * @param props - Props for the landmark.\n * @param ref - Ref to the landmark.\n */\nexport function useLandmark(props: AriaLandmarkProps, ref: MutableRefObject<FocusableElement>): LandmarkAria {\n const {\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n focus\n } = props;\n let manager = useLandmarkManager();\n let label = ariaLabel || ariaLabelledby;\n let [isLandmarkFocused, setIsLandmarkFocused] = useState(false);\n\n let defaultFocus = useCallback(() => {\n setIsLandmarkFocused(true);\n }, [setIsLandmarkFocused]);\n\n let blur = useCallback(() => {\n setIsLandmarkFocused(false);\n }, [setIsLandmarkFocused]);\n\n useLayoutEffect(() => {\n return manager.registerLandmark({ref, label, role, focus: focus || defaultFocus, blur});\n }, [manager, label, ref, role, focus, defaultFocus, blur]);\n\n useEffect(() => {\n if (isLandmarkFocused) {\n ref.current.focus();\n }\n }, [isLandmarkFocused, ref]);\n\n return {\n landmarkProps: {\n role,\n tabIndex: isLandmarkFocused ? -1 : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby\n }\n };\n}\n"],"names":[],"version":3,"file":"main.js.map"}
|
|
1
|
+
{"mappings":"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","sources":["packages/@react-aria/landmark/src/index.ts","packages/@react-aria/landmark/src/useLandmark.ts"],"sourcesContent":["/*\n * Copyright 2022 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport type {AriaLandmarkRole, AriaLandmarkProps, LandmarkAria, LandmarkController} from './useLandmark';\nexport {useLandmark, createLandmarkController} from './useLandmark';\n","/*\n * Copyright 2022 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {AriaLabelingProps, DOMAttributes, FocusableElement} from '@react-types/shared';\nimport {RefObject, useCallback, useEffect, useState} from 'react';\nimport {useLayoutEffect} from '@react-aria/utils';\nimport {useSyncExternalStore} from 'use-sync-external-store/shim/index.js';\n\nexport type AriaLandmarkRole = 'main' | 'region' | 'search' | 'navigation' | 'form' | 'banner' | 'contentinfo' | 'complementary';\n\nexport interface AriaLandmarkProps extends AriaLabelingProps {\n role: AriaLandmarkRole,\n focus?: (direction: 'forward' | 'backward') => void\n}\n\nexport interface LandmarkAria {\n landmarkProps: DOMAttributes\n}\n\n// Increment this version number whenever the\n// LandmarkManagerApi or Landmark interfaces change.\nconst LANDMARK_API_VERSION = 1;\n\n// Minimal API for LandmarkManager that must continue to work between versions.\n// Changes to this interface are considered breaking. New methods/properties are\n// safe to add, but changes or removals are not allowed (same as public APIs).\ninterface LandmarkManagerApi {\n version: number,\n createLandmarkController(): LandmarkController,\n registerLandmark(landmark: Landmark): () => void\n}\n\n// Changes to this interface are considered breaking.\n// New properties MUST be optional so that registering a landmark\n// from an older version of useLandmark against a newer version of\n// LandmarkManager does not crash.\ninterface Landmark {\n ref: RefObject<FocusableElement>,\n role: AriaLandmarkRole,\n label?: string,\n lastFocused?: FocusableElement,\n focus: (direction: 'forward' | 'backward') => void,\n blur: () => void\n}\n\nexport interface LandmarkControllerOptions {\n /**\n * The element from which to start navigating.\n * @default document.activeElement\n */\n from?: FocusableElement\n}\n\n/** A LandmarkController allows programmatic navigation of landmarks. */\nexport interface LandmarkController {\n /** Moves focus to the next landmark. */\n focusNext(opts?: LandmarkControllerOptions): boolean,\n /** Moves focus to the previous landmark. */\n focusPrevious(opts?: LandmarkControllerOptions): boolean,\n /** Moves focus to the main landmark. */\n focusMain(): boolean,\n /** Moves focus either forward or backward in the landmark sequence. */\n navigate(direction: 'forward' | 'backward', opts?: LandmarkControllerOptions): boolean,\n /**\n * Disposes the landmark controller. When no landmarks are registered, and no\n * controllers are active, the landmark keyboard listeners are removed from the page.\n */\n dispose(): void\n}\n\n// Symbol under which the singleton landmark manager instance is attached to the document.\nconst landmarkSymbol = Symbol.for('react-aria-landmark-manager');\n\nfunction subscribe(fn: () => void) {\n document.addEventListener('react-aria-landmark-manager-change', fn);\n return () => document.removeEventListener('react-aria-landmark-manager-change', fn);\n}\n\nfunction getLandmarkManager(): LandmarkManagerApi | null {\n if (typeof document === 'undefined') {\n return null;\n }\n\n // Reuse an existing instance if it has the same or greater version.\n let instance = document[landmarkSymbol];\n if (instance && instance.version >= LANDMARK_API_VERSION) {\n return instance;\n }\n\n // Otherwise, create a new instance and dispatch an event so anything using the existing\n // instance updates and re-registers their landmarks with the new one.\n document[landmarkSymbol] = new LandmarkManager();\n document.dispatchEvent(new CustomEvent('react-aria-landmark-manager-change'));\n return document[landmarkSymbol];\n}\n\n// Subscribes a React component to the current landmark manager instance.\nfunction useLandmarkManager(): LandmarkManagerApi | null {\n return useSyncExternalStore(subscribe, getLandmarkManager, getLandmarkManager);\n}\n\nclass LandmarkManager implements LandmarkManagerApi {\n private landmarks: Array<Landmark> = [];\n private isListening = false;\n private refCount = 0;\n public version = LANDMARK_API_VERSION;\n\n constructor() {\n this.f6Handler = this.f6Handler.bind(this);\n this.focusinHandler = this.focusinHandler.bind(this);\n this.focusoutHandler = this.focusoutHandler.bind(this);\n }\n\n private setupIfNeeded() {\n if (this.isListening) {\n return;\n }\n document.addEventListener('keydown', this.f6Handler, {capture: true});\n document.addEventListener('focusin', this.focusinHandler, {capture: true});\n document.addEventListener('focusout', this.focusoutHandler, {capture: true});\n this.isListening = true;\n }\n\n private teardownIfNeeded() {\n if (!this.isListening || this.landmarks.length > 0 || this.refCount > 0) {\n return;\n }\n document.removeEventListener('keydown', this.f6Handler, {capture: true});\n document.removeEventListener('focusin', this.focusinHandler, {capture: true});\n document.removeEventListener('focusout', this.focusoutHandler, {capture: true});\n this.isListening = false;\n }\n\n private focusLandmark(landmark: FocusableElement, direction: 'forward' | 'backward') {\n this.landmarks.find(l => l.ref.current === landmark)?.focus?.(direction);\n }\n\n /**\n * Return set of landmarks with a specific role.\n */\n private getLandmarksByRole(role: AriaLandmarkRole) {\n return new Set(this.landmarks.filter(l => l.role === role));\n }\n\n /**\n * Return first landmark with a specific role.\n */\n private getLandmarkByRole(role: AriaLandmarkRole) {\n return this.landmarks.find(l => l.role === role);\n }\n\n private addLandmark(newLandmark: Landmark) {\n this.setupIfNeeded();\n if (this.landmarks.find(landmark => landmark.ref === newLandmark.ref) || !newLandmark.ref.current) {\n return;\n }\n\n if (this.landmarks.filter(landmark => landmark.role === 'main').length > 1) {\n console.error('Page can contain no more than one landmark with the role \"main\".');\n }\n\n if (this.landmarks.length === 0) {\n this.landmarks = [newLandmark];\n this.checkLabels(newLandmark.role);\n return;\n }\n\n\n // Binary search to insert new landmark based on position in document relative to existing landmarks.\n // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition\n let start = 0;\n let end = this.landmarks.length - 1;\n while (start <= end) {\n let mid = Math.floor((start + end) / 2);\n let comparedPosition = newLandmark.ref.current.compareDocumentPosition(this.landmarks[mid].ref.current as Node);\n let isNewAfterExisting = Boolean((comparedPosition & Node.DOCUMENT_POSITION_PRECEDING) || (comparedPosition & Node.DOCUMENT_POSITION_CONTAINS));\n\n if (isNewAfterExisting) {\n start = mid + 1;\n } else {\n end = mid - 1;\n }\n }\n\n this.landmarks.splice(start, 0, newLandmark);\n this.checkLabels(newLandmark.role);\n }\n\n private updateLandmark(landmark: Pick<Landmark, 'ref'> & Partial<Landmark>) {\n let index = this.landmarks.findIndex(l => l.ref === landmark.ref);\n if (index >= 0) {\n this.landmarks[index] = {...this.landmarks[index], ...landmark};\n this.checkLabels(this.landmarks[index].role);\n }\n }\n\n private removeLandmark(ref: RefObject<Element>) {\n this.landmarks = this.landmarks.filter(landmark => landmark.ref !== ref);\n this.teardownIfNeeded();\n }\n\n /**\n * Warn if there are 2+ landmarks with the same role but no label.\n * Labels for landmarks with the same role must also be unique.\n *\n * See https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/.\n */\n private checkLabels(role: AriaLandmarkRole) {\n let landmarksWithRole = this.getLandmarksByRole(role);\n if (landmarksWithRole.size > 1) {\n let duplicatesWithoutLabel = [...landmarksWithRole].filter(landmark => !landmark.label);\n if (duplicatesWithoutLabel.length > 0) {\n console.warn(\n `Page contains more than one landmark with the '${role}' role. If two or more landmarks on a page share the same role, all must be labeled with an aria-label or aria-labelledby attribute: `,\n duplicatesWithoutLabel.map(landmark => landmark.ref.current)\n );\n } else {\n let labels = [...landmarksWithRole].map(landmark => landmark.label);\n let duplicateLabels = labels.filter((item, index) => labels.indexOf(item) !== index);\n\n duplicateLabels.forEach((label) => {\n console.warn(\n `Page contains more than one landmark with the '${role}' role and '${label}' label. If two or more landmarks on a page share the same role, they must have unique labels: `,\n [...landmarksWithRole].filter(landmark => landmark.label === label).map(landmark => landmark.ref.current)\n );\n });\n }\n }\n }\n\n /**\n * Get the landmark that is the closest parent in the DOM.\n * Returns undefined if no parent is a landmark.\n */\n private closestLandmark(element: FocusableElement) {\n let landmarkMap = new Map(this.landmarks.map(l => [l.ref.current, l]));\n let currentElement = element;\n while (currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body && currentElement.parentElement) {\n currentElement = currentElement.parentElement;\n }\n return landmarkMap.get(currentElement);\n }\n\n /**\n * Gets the next landmark, in DOM focus order, or previous if backwards is specified.\n * If last landmark, next should be the first landmark.\n * If not inside a landmark, will return first landmark.\n * Returns undefined if there are no landmarks.\n */\n private getNextLandmark(element: FocusableElement, {backward}: {backward?: boolean }) {\n let currentLandmark = this.closestLandmark(element);\n let nextLandmarkIndex = backward ? this.landmarks.length - 1 : 0;\n if (currentLandmark) {\n nextLandmarkIndex = this.landmarks.indexOf(currentLandmark) + (backward ? -1 : 1);\n }\n\n let wrapIfNeeded = () => {\n // When we reach the end of the landmark sequence, fire a custom event that can be listened for by applications.\n // If this event is canceled, we return immediately. This can be used to implement landmark navigation across iframes.\n if (nextLandmarkIndex < 0) {\n if (!element.dispatchEvent(new CustomEvent('react-aria-landmark-navigation', {detail: {direction: 'backward'}, bubbles: true, cancelable: true}))) {\n return true;\n }\n\n nextLandmarkIndex = this.landmarks.length - 1;\n } else if (nextLandmarkIndex >= this.landmarks.length) {\n if (!element.dispatchEvent(new CustomEvent('react-aria-landmark-navigation', {detail: {direction: 'forward'}, bubbles: true, cancelable: true}))) {\n return true;\n }\n\n nextLandmarkIndex = 0;\n }\n\n if (nextLandmarkIndex < 0 || nextLandmarkIndex >= this.landmarks.length) {\n return true;\n }\n\n return false;\n };\n\n if (wrapIfNeeded()) {\n return undefined;\n }\n\n // Skip over hidden landmarks.\n let i = nextLandmarkIndex;\n while (this.landmarks[nextLandmarkIndex].ref.current?.closest('[aria-hidden=true]')) {\n nextLandmarkIndex += backward ? -1 : 1;\n if (wrapIfNeeded()) {\n return undefined;\n }\n\n if (nextLandmarkIndex === i) {\n break;\n }\n }\n\n return this.landmarks[nextLandmarkIndex];\n }\n\n /**\n * Look at next landmark. If an element was previously focused inside, restore focus there.\n * If not, focus the landmark itself.\n * If no landmarks at all, or none with focusable elements, don't move focus.\n */\n private f6Handler(e: KeyboardEvent) {\n if (e.key === 'F6') {\n // If alt key pressed, focus main landmark, otherwise navigate forward or backward based on shift key.\n let handled = e.altKey ? this.focusMain() : this.navigate(e.target as FocusableElement, e.shiftKey);\n if (handled) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n }\n\n private focusMain() {\n let main = this.getLandmarkByRole('main');\n if (main && main.ref.current && document.contains(main.ref.current)) {\n this.focusLandmark(main.ref.current, 'forward');\n return true;\n }\n\n return false;\n }\n\n private navigate(from: FocusableElement, backward: boolean) {\n let nextLandmark = this.getNextLandmark(from, {\n backward\n });\n\n if (!nextLandmark) {\n return false;\n }\n\n // If something was previously focused in the next landmark, then return focus to it\n if (nextLandmark.lastFocused) {\n let lastFocused = nextLandmark.lastFocused;\n if (document.body.contains(lastFocused)) {\n lastFocused.focus();\n return true;\n }\n }\n\n // Otherwise, focus the landmark itself\n if (nextLandmark.ref.current && document.contains(nextLandmark.ref.current)) {\n this.focusLandmark(nextLandmark.ref.current, backward ? 'backward' : 'forward');\n return true;\n }\n\n return false;\n }\n\n /**\n * Sets lastFocused for a landmark, if focus is moved within that landmark.\n * Lets the last focused landmark know it was blurred if something else is focused.\n */\n private focusinHandler(e: FocusEvent) {\n let currentLandmark = this.closestLandmark(e.target as FocusableElement);\n if (currentLandmark && currentLandmark.ref.current !== e.target) {\n this.updateLandmark({ref: currentLandmark.ref, lastFocused: e.target as FocusableElement});\n }\n let previousFocusedElement = e.relatedTarget as FocusableElement;\n if (previousFocusedElement) {\n let closestPreviousLandmark = this.closestLandmark(previousFocusedElement);\n if (closestPreviousLandmark && closestPreviousLandmark.ref.current === previousFocusedElement) {\n closestPreviousLandmark.blur();\n }\n }\n }\n\n /**\n * Track if the focus is lost to the body. If it is, do cleanup on the landmark that last had focus.\n */\n private focusoutHandler(e: FocusEvent) {\n let previousFocusedElement = e.target as FocusableElement;\n let nextFocusedElement = e.relatedTarget;\n // the === document seems to be a jest thing for focus to go there on generic blur event such as landmark.blur();\n // browsers appear to send focus instead to document.body and the relatedTarget is null when that happens\n if (!nextFocusedElement || nextFocusedElement === document) {\n let closestPreviousLandmark = this.closestLandmark(previousFocusedElement);\n if (closestPreviousLandmark && closestPreviousLandmark.ref.current === previousFocusedElement) {\n closestPreviousLandmark.blur();\n }\n }\n }\n\n public createLandmarkController(): LandmarkController {\n let instance: LandmarkManager | null = this;\n instance.refCount++;\n instance.setupIfNeeded();\n return {\n navigate(direction, opts) {\n let element = opts?.from || (document!.activeElement as FocusableElement);\n return instance!.navigate(element, direction === 'backward');\n },\n focusNext(opts) {\n let element = opts?.from || (document!.activeElement as FocusableElement);\n return instance!.navigate(element, false);\n },\n focusPrevious(opts) {\n let element = opts?.from || (document!.activeElement as FocusableElement);\n return instance!.navigate(element, true);\n },\n focusMain() {\n return instance!.focusMain();\n },\n dispose() {\n if (instance) {\n instance.refCount--;\n instance.teardownIfNeeded();\n instance = null;\n }\n }\n };\n }\n\n public registerLandmark(landmark: Landmark): () => void {\n if (this.landmarks.find(l => l.ref === landmark.ref)) {\n this.updateLandmark(landmark);\n } else {\n this.addLandmark(landmark);\n }\n\n return () => this.removeLandmark(landmark.ref);\n }\n}\n\n/** Creates a LandmarkController, which allows programmatic navigation of landmarks. */\nexport function createLandmarkController(): LandmarkController {\n // Get the current landmark manager and create a controller using it.\n let instance: LandmarkManagerApi | null = getLandmarkManager();\n let controller = instance?.createLandmarkController();\n\n let unsubscribe = subscribe(() => {\n // If the landmark manager changes, dispose the old\n // controller and create a new one.\n controller?.dispose();\n instance = getLandmarkManager();\n controller = instance?.createLandmarkController();\n });\n\n // Return a wrapper that proxies requests to the current controller instance.\n return {\n navigate(direction, opts) {\n return controller!.navigate(direction, opts);\n },\n focusNext(opts) {\n return controller!.focusNext(opts);\n },\n focusPrevious(opts) {\n return controller!.focusPrevious(opts);\n },\n focusMain() {\n return controller!.focusMain();\n },\n dispose() {\n controller?.dispose();\n unsubscribe();\n controller = undefined;\n instance = null;\n }\n };\n}\n\n/**\n * Provides landmark navigation in an application. Call this with a role and label to register a landmark navigable with F6.\n * @param props - Props for the landmark.\n * @param ref - Ref to the landmark.\n */\nexport function useLandmark(props: AriaLandmarkProps, ref: RefObject<FocusableElement>): LandmarkAria {\n const {\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n focus\n } = props;\n let manager = useLandmarkManager();\n let label = ariaLabel || ariaLabelledby;\n let [isLandmarkFocused, setIsLandmarkFocused] = useState(false);\n\n let defaultFocus = useCallback(() => {\n setIsLandmarkFocused(true);\n }, [setIsLandmarkFocused]);\n\n let blur = useCallback(() => {\n setIsLandmarkFocused(false);\n }, [setIsLandmarkFocused]);\n\n useLayoutEffect(() => {\n if (manager) {\n return manager.registerLandmark({ref, label, role, focus: focus || defaultFocus, blur});\n }\n }, [manager, label, ref, role, focus, defaultFocus, blur]);\n\n useEffect(() => {\n if (isLandmarkFocused) {\n ref.current?.focus();\n }\n }, [isLandmarkFocused, ref]);\n\n return {\n landmarkProps: {\n role,\n tabIndex: isLandmarkFocused ? -1 : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby\n }\n };\n}\n"],"names":[],"version":3,"file":"main.js.map"}
|
package/dist/module.js
CHANGED
|
@@ -79,8 +79,8 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
79
79
|
this.isListening = false;
|
|
80
80
|
}
|
|
81
81
|
focusLandmark(landmark, direction) {
|
|
82
|
-
var _this_landmarks_find;
|
|
83
|
-
(_this_landmarks_find = this.landmarks.find((l)=>l.ref.current === landmark)) === null || _this_landmarks_find === void 0 ? void 0 : _this_landmarks_find.focus(direction);
|
|
82
|
+
var _this_landmarks_find, _this_landmarks_find_focus;
|
|
83
|
+
(_this_landmarks_find = this.landmarks.find((l)=>l.ref.current === landmark)) === null || _this_landmarks_find === void 0 ? void 0 : (_this_landmarks_find_focus = _this_landmarks_find.focus) === null || _this_landmarks_find_focus === void 0 ? void 0 : _this_landmarks_find_focus.call(_this_landmarks_find, direction);
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
86
86
|
* Return set of landmarks with a specific role.
|
|
@@ -94,7 +94,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
94
94
|
}
|
|
95
95
|
addLandmark(newLandmark) {
|
|
96
96
|
this.setupIfNeeded();
|
|
97
|
-
if (this.landmarks.find((landmark)=>landmark.ref === newLandmark.ref)) return;
|
|
97
|
+
if (this.landmarks.find((landmark)=>landmark.ref === newLandmark.ref) || !newLandmark.ref.current) return;
|
|
98
98
|
if (this.landmarks.filter((landmark)=>landmark.role === "main").length > 1) console.error('Page can contain no more than one landmark with the role "main".');
|
|
99
99
|
if (this.landmarks.length === 0) {
|
|
100
100
|
this.landmarks = [
|
|
@@ -165,7 +165,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
165
165
|
l
|
|
166
166
|
]));
|
|
167
167
|
let currentElement = element;
|
|
168
|
-
while(currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body)currentElement = currentElement.parentElement;
|
|
168
|
+
while(currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body && currentElement.parentElement)currentElement = currentElement.parentElement;
|
|
169
169
|
return landmarkMap.get(currentElement);
|
|
170
170
|
}
|
|
171
171
|
/**
|
|
@@ -174,6 +174,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
174
174
|
* If not inside a landmark, will return first landmark.
|
|
175
175
|
* Returns undefined if there are no landmarks.
|
|
176
176
|
*/ getNextLandmark(element, { backward: backward }) {
|
|
177
|
+
var _this_landmarks_nextLandmarkIndex_ref_current;
|
|
177
178
|
let currentLandmark = this.closestLandmark(element);
|
|
178
179
|
let nextLandmarkIndex = backward ? this.landmarks.length - 1 : 0;
|
|
179
180
|
if (currentLandmark) nextLandmarkIndex = this.landmarks.indexOf(currentLandmark) + (backward ? -1 : 1);
|
|
@@ -205,7 +206,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
205
206
|
if (wrapIfNeeded()) return undefined;
|
|
206
207
|
// Skip over hidden landmarks.
|
|
207
208
|
let i = nextLandmarkIndex;
|
|
208
|
-
while(this.landmarks[nextLandmarkIndex].ref.current.closest("[aria-hidden=true]")){
|
|
209
|
+
while((_this_landmarks_nextLandmarkIndex_ref_current = this.landmarks[nextLandmarkIndex].ref.current) === null || _this_landmarks_nextLandmarkIndex_ref_current === void 0 ? void 0 : _this_landmarks_nextLandmarkIndex_ref_current.closest("[aria-hidden=true]")){
|
|
209
210
|
nextLandmarkIndex += backward ? -1 : 1;
|
|
210
211
|
if (wrapIfNeeded()) return undefined;
|
|
211
212
|
if (nextLandmarkIndex === i) break;
|
|
@@ -228,7 +229,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
228
229
|
}
|
|
229
230
|
focusMain() {
|
|
230
231
|
let main = this.getLandmarkByRole("main");
|
|
231
|
-
if (main && document.contains(main.ref.current)) {
|
|
232
|
+
if (main && main.ref.current && document.contains(main.ref.current)) {
|
|
232
233
|
this.focusLandmark(main.ref.current, "forward");
|
|
233
234
|
return true;
|
|
234
235
|
}
|
|
@@ -248,7 +249,7 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
248
249
|
}
|
|
249
250
|
}
|
|
250
251
|
// Otherwise, focus the landmark itself
|
|
251
|
-
if (document.contains(nextLandmark.ref.current)) {
|
|
252
|
+
if (nextLandmark.ref.current && document.contains(nextLandmark.ref.current)) {
|
|
252
253
|
this.focusLandmark(nextLandmark.ref.current, backward ? "backward" : "forward");
|
|
253
254
|
return true;
|
|
254
255
|
}
|
|
@@ -287,21 +288,26 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
287
288
|
instance.setupIfNeeded();
|
|
288
289
|
return {
|
|
289
290
|
navigate (direction, opts) {
|
|
290
|
-
|
|
291
|
+
let element = (opts === null || opts === void 0 ? void 0 : opts.from) || document.activeElement;
|
|
292
|
+
return instance.navigate(element, direction === "backward");
|
|
291
293
|
},
|
|
292
294
|
focusNext (opts) {
|
|
293
|
-
|
|
295
|
+
let element = (opts === null || opts === void 0 ? void 0 : opts.from) || document.activeElement;
|
|
296
|
+
return instance.navigate(element, false);
|
|
294
297
|
},
|
|
295
298
|
focusPrevious (opts) {
|
|
296
|
-
|
|
299
|
+
let element = (opts === null || opts === void 0 ? void 0 : opts.from) || document.activeElement;
|
|
300
|
+
return instance.navigate(element, true);
|
|
297
301
|
},
|
|
298
302
|
focusMain () {
|
|
299
303
|
return instance.focusMain();
|
|
300
304
|
},
|
|
301
305
|
dispose () {
|
|
302
|
-
instance
|
|
303
|
-
|
|
304
|
-
|
|
306
|
+
if (instance) {
|
|
307
|
+
instance.refCount--;
|
|
308
|
+
instance.teardownIfNeeded();
|
|
309
|
+
instance = null;
|
|
310
|
+
}
|
|
305
311
|
}
|
|
306
312
|
};
|
|
307
313
|
}
|
|
@@ -323,13 +329,13 @@ class $a86207c5d7f7e1fb$var$LandmarkManager {
|
|
|
323
329
|
function $a86207c5d7f7e1fb$export$f50151dbd51cd1d9() {
|
|
324
330
|
// Get the current landmark manager and create a controller using it.
|
|
325
331
|
let instance = $a86207c5d7f7e1fb$var$getLandmarkManager();
|
|
326
|
-
let controller = instance.createLandmarkController();
|
|
332
|
+
let controller = instance === null || instance === void 0 ? void 0 : instance.createLandmarkController();
|
|
327
333
|
let unsubscribe = $a86207c5d7f7e1fb$var$subscribe(()=>{
|
|
328
334
|
// If the landmark manager changes, dispose the old
|
|
329
335
|
// controller and create a new one.
|
|
330
|
-
controller.dispose();
|
|
336
|
+
controller === null || controller === void 0 ? void 0 : controller.dispose();
|
|
331
337
|
instance = $a86207c5d7f7e1fb$var$getLandmarkManager();
|
|
332
|
-
controller = instance.createLandmarkController();
|
|
338
|
+
controller = instance === null || instance === void 0 ? void 0 : instance.createLandmarkController();
|
|
333
339
|
});
|
|
334
340
|
// Return a wrapper that proxies requests to the current controller instance.
|
|
335
341
|
return {
|
|
@@ -346,9 +352,9 @@ function $a86207c5d7f7e1fb$export$f50151dbd51cd1d9() {
|
|
|
346
352
|
return controller.focusMain();
|
|
347
353
|
},
|
|
348
354
|
dispose () {
|
|
349
|
-
controller.dispose();
|
|
355
|
+
controller === null || controller === void 0 ? void 0 : controller.dispose();
|
|
350
356
|
unsubscribe();
|
|
351
|
-
controller =
|
|
357
|
+
controller = undefined;
|
|
352
358
|
instance = null;
|
|
353
359
|
}
|
|
354
360
|
};
|
|
@@ -369,7 +375,7 @@ function $a86207c5d7f7e1fb$export$4cc632584fd87fae(props, ref) {
|
|
|
369
375
|
setIsLandmarkFocused
|
|
370
376
|
]);
|
|
371
377
|
(0, $TvsbU$useLayoutEffect)(()=>{
|
|
372
|
-
return manager.registerLandmark({
|
|
378
|
+
if (manager) return manager.registerLandmark({
|
|
373
379
|
ref: ref,
|
|
374
380
|
label: label,
|
|
375
381
|
role: role,
|
|
@@ -386,7 +392,8 @@ function $a86207c5d7f7e1fb$export$4cc632584fd87fae(props, ref) {
|
|
|
386
392
|
blur
|
|
387
393
|
]);
|
|
388
394
|
(0, $TvsbU$useEffect)(()=>{
|
|
389
|
-
|
|
395
|
+
var _ref_current;
|
|
396
|
+
if (isLandmarkFocused) (_ref_current = ref.current) === null || _ref_current === void 0 ? void 0 : _ref_current.focus();
|
|
390
397
|
}, [
|
|
391
398
|
isLandmarkFocused,
|
|
392
399
|
ref
|
package/dist/module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","sources":["packages/@react-aria/landmark/src/index.ts","packages/@react-aria/landmark/src/useLandmark.ts"],"sourcesContent":["/*\n * Copyright 2022 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport type {AriaLandmarkRole, AriaLandmarkProps, LandmarkAria, LandmarkController} from './useLandmark';\nexport {useLandmark, createLandmarkController} from './useLandmark';\n","/*\n * Copyright 2022 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {AriaLabelingProps, DOMAttributes, FocusableElement} from '@react-types/shared';\nimport {MutableRefObject, useCallback, useEffect, useState} from 'react';\nimport {useLayoutEffect} from '@react-aria/utils';\nimport {useSyncExternalStore} from 'use-sync-external-store/shim/index.js';\n\nexport type AriaLandmarkRole = 'main' | 'region' | 'search' | 'navigation' | 'form' | 'banner' | 'contentinfo' | 'complementary';\n\nexport interface AriaLandmarkProps extends AriaLabelingProps {\n role: AriaLandmarkRole,\n focus?: (direction: 'forward' | 'backward') => void\n}\n\nexport interface LandmarkAria {\n landmarkProps: DOMAttributes\n}\n\n// Increment this version number whenever the\n// LandmarkManagerApi or Landmark interfaces change.\nconst LANDMARK_API_VERSION = 1;\n\n// Minimal API for LandmarkManager that must continue to work between versions.\n// Changes to this interface are considered breaking. New methods/properties are\n// safe to add, but changes or removals are not allowed (same as public APIs).\ninterface LandmarkManagerApi {\n version: number,\n createLandmarkController(): LandmarkController,\n registerLandmark(landmark: Landmark): () => void\n}\n\n// Changes to this interface are considered breaking.\n// New properties MUST be optional so that registering a landmark\n// from an older version of useLandmark against a newer version of\n// LandmarkManager does not crash.\ninterface Landmark {\n ref: MutableRefObject<Element>,\n role: AriaLandmarkRole,\n label?: string,\n lastFocused?: FocusableElement,\n focus: (direction: 'forward' | 'backward') => void,\n blur: () => void\n}\n\nexport interface LandmarkControllerOptions {\n /**\n * The element from which to start navigating.\n * @default document.activeElement\n */\n from?: Element\n}\n\n/** A LandmarkController allows programmatic navigation of landmarks. */\nexport interface LandmarkController {\n /** Moves focus to the next landmark. */\n focusNext(opts?: LandmarkControllerOptions): boolean,\n /** Moves focus to the previous landmark. */\n focusPrevious(opts?: LandmarkControllerOptions): boolean,\n /** Moves focus to the main landmark. */\n focusMain(): boolean,\n /** Moves focus either forward or backward in the landmark sequence. */\n navigate(direction: 'forward' | 'backward', opts?: LandmarkControllerOptions): boolean,\n /**\n * Disposes the landmark controller. When no landmarks are registered, and no\n * controllers are active, the landmark keyboard listeners are removed from the page.\n */\n dispose(): void\n}\n\n// Symbol under which the singleton landmark manager instance is attached to the document.\nconst landmarkSymbol = Symbol.for('react-aria-landmark-manager');\n\nfunction subscribe(fn: () => void) {\n document.addEventListener('react-aria-landmark-manager-change', fn);\n return () => document.removeEventListener('react-aria-landmark-manager-change', fn);\n}\n\nfunction getLandmarkManager(): LandmarkManagerApi | null {\n if (typeof document === 'undefined') {\n return null;\n }\n\n // Reuse an existing instance if it has the same or greater version.\n let instance = document[landmarkSymbol];\n if (instance && instance.version >= LANDMARK_API_VERSION) {\n return instance;\n }\n\n // Otherwise, create a new instance and dispatch an event so anything using the existing\n // instance updates and re-registers their landmarks with the new one.\n document[landmarkSymbol] = new LandmarkManager();\n document.dispatchEvent(new CustomEvent('react-aria-landmark-manager-change'));\n return document[landmarkSymbol];\n}\n\n// Subscribes a React component to the current landmark manager instance.\nfunction useLandmarkManager(): LandmarkManagerApi | null {\n return useSyncExternalStore(subscribe, getLandmarkManager, getLandmarkManager);\n}\n\nclass LandmarkManager implements LandmarkManagerApi {\n private landmarks: Array<Landmark> = [];\n private isListening = false;\n private refCount = 0;\n public version = LANDMARK_API_VERSION;\n\n constructor() {\n this.f6Handler = this.f6Handler.bind(this);\n this.focusinHandler = this.focusinHandler.bind(this);\n this.focusoutHandler = this.focusoutHandler.bind(this);\n }\n\n private setupIfNeeded() {\n if (this.isListening) {\n return;\n }\n document.addEventListener('keydown', this.f6Handler, {capture: true});\n document.addEventListener('focusin', this.focusinHandler, {capture: true});\n document.addEventListener('focusout', this.focusoutHandler, {capture: true});\n this.isListening = true;\n }\n\n private teardownIfNeeded() {\n if (!this.isListening || this.landmarks.length > 0 || this.refCount > 0) {\n return;\n }\n document.removeEventListener('keydown', this.f6Handler, {capture: true});\n document.removeEventListener('focusin', this.focusinHandler, {capture: true});\n document.removeEventListener('focusout', this.focusoutHandler, {capture: true});\n this.isListening = false;\n }\n\n private focusLandmark(landmark: Element, direction: 'forward' | 'backward') {\n this.landmarks.find(l => l.ref.current === landmark)?.focus(direction);\n }\n\n /**\n * Return set of landmarks with a specific role.\n */\n private getLandmarksByRole(role: AriaLandmarkRole) {\n return new Set(this.landmarks.filter(l => l.role === role));\n }\n\n /**\n * Return first landmark with a specific role.\n */\n private getLandmarkByRole(role: AriaLandmarkRole) {\n return this.landmarks.find(l => l.role === role);\n }\n\n private addLandmark(newLandmark: Landmark) {\n this.setupIfNeeded();\n if (this.landmarks.find(landmark => landmark.ref === newLandmark.ref)) {\n return;\n }\n\n if (this.landmarks.filter(landmark => landmark.role === 'main').length > 1) {\n console.error('Page can contain no more than one landmark with the role \"main\".');\n }\n\n if (this.landmarks.length === 0) {\n this.landmarks = [newLandmark];\n this.checkLabels(newLandmark.role);\n return;\n }\n\n\n // Binary search to insert new landmark based on position in document relative to existing landmarks.\n // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition\n let start = 0;\n let end = this.landmarks.length - 1;\n while (start <= end) {\n let mid = Math.floor((start + end) / 2);\n let comparedPosition = newLandmark.ref.current.compareDocumentPosition(this.landmarks[mid].ref.current as Node);\n let isNewAfterExisting = Boolean((comparedPosition & Node.DOCUMENT_POSITION_PRECEDING) || (comparedPosition & Node.DOCUMENT_POSITION_CONTAINS));\n\n if (isNewAfterExisting) {\n start = mid + 1;\n } else {\n end = mid - 1;\n }\n }\n\n this.landmarks.splice(start, 0, newLandmark);\n this.checkLabels(newLandmark.role);\n }\n\n private updateLandmark(landmark: Pick<Landmark, 'ref'> & Partial<Landmark>) {\n let index = this.landmarks.findIndex(l => l.ref === landmark.ref);\n if (index >= 0) {\n this.landmarks[index] = {...this.landmarks[index], ...landmark};\n this.checkLabels(this.landmarks[index].role);\n }\n }\n\n private removeLandmark(ref: MutableRefObject<Element>) {\n this.landmarks = this.landmarks.filter(landmark => landmark.ref !== ref);\n this.teardownIfNeeded();\n }\n\n /**\n * Warn if there are 2+ landmarks with the same role but no label.\n * Labels for landmarks with the same role must also be unique.\n *\n * See https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/.\n */\n private checkLabels(role: AriaLandmarkRole) {\n let landmarksWithRole = this.getLandmarksByRole(role);\n if (landmarksWithRole.size > 1) {\n let duplicatesWithoutLabel = [...landmarksWithRole].filter(landmark => !landmark.label);\n if (duplicatesWithoutLabel.length > 0) {\n console.warn(\n `Page contains more than one landmark with the '${role}' role. If two or more landmarks on a page share the same role, all must be labeled with an aria-label or aria-labelledby attribute: `,\n duplicatesWithoutLabel.map(landmark => landmark.ref.current)\n );\n } else {\n let labels = [...landmarksWithRole].map(landmark => landmark.label);\n let duplicateLabels = labels.filter((item, index) => labels.indexOf(item) !== index);\n\n duplicateLabels.forEach((label) => {\n console.warn(\n `Page contains more than one landmark with the '${role}' role and '${label}' label. If two or more landmarks on a page share the same role, they must have unique labels: `,\n [...landmarksWithRole].filter(landmark => landmark.label === label).map(landmark => landmark.ref.current)\n );\n });\n }\n }\n }\n\n /**\n * Get the landmark that is the closest parent in the DOM.\n * Returns undefined if no parent is a landmark.\n */\n private closestLandmark(element: Element) {\n let landmarkMap = new Map(this.landmarks.map(l => [l.ref.current, l]));\n let currentElement = element;\n while (currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body) {\n currentElement = currentElement.parentElement;\n }\n return landmarkMap.get(currentElement);\n }\n\n /**\n * Gets the next landmark, in DOM focus order, or previous if backwards is specified.\n * If last landmark, next should be the first landmark.\n * If not inside a landmark, will return first landmark.\n * Returns undefined if there are no landmarks.\n */\n private getNextLandmark(element: Element, {backward}: {backward?: boolean }) {\n let currentLandmark = this.closestLandmark(element);\n let nextLandmarkIndex = backward ? this.landmarks.length - 1 : 0;\n if (currentLandmark) {\n nextLandmarkIndex = this.landmarks.indexOf(currentLandmark) + (backward ? -1 : 1);\n }\n\n let wrapIfNeeded = () => {\n // When we reach the end of the landmark sequence, fire a custom event that can be listened for by applications.\n // If this event is canceled, we return immediately. This can be used to implement landmark navigation across iframes.\n if (nextLandmarkIndex < 0) {\n if (!element.dispatchEvent(new CustomEvent('react-aria-landmark-navigation', {detail: {direction: 'backward'}, bubbles: true, cancelable: true}))) {\n return true;\n }\n\n nextLandmarkIndex = this.landmarks.length - 1;\n } else if (nextLandmarkIndex >= this.landmarks.length) {\n if (!element.dispatchEvent(new CustomEvent('react-aria-landmark-navigation', {detail: {direction: 'forward'}, bubbles: true, cancelable: true}))) {\n return true;\n }\n\n nextLandmarkIndex = 0;\n }\n\n if (nextLandmarkIndex < 0 || nextLandmarkIndex >= this.landmarks.length) {\n return true;\n }\n\n return false;\n };\n\n if (wrapIfNeeded()) {\n return undefined;\n }\n\n // Skip over hidden landmarks.\n let i = nextLandmarkIndex;\n while (this.landmarks[nextLandmarkIndex].ref.current.closest('[aria-hidden=true]')) {\n nextLandmarkIndex += backward ? -1 : 1;\n if (wrapIfNeeded()) {\n return undefined;\n }\n\n if (nextLandmarkIndex === i) {\n break;\n }\n }\n\n return this.landmarks[nextLandmarkIndex];\n }\n\n /**\n * Look at next landmark. If an element was previously focused inside, restore focus there.\n * If not, focus the landmark itself.\n * If no landmarks at all, or none with focusable elements, don't move focus.\n */\n private f6Handler(e: KeyboardEvent) {\n if (e.key === 'F6') {\n // If alt key pressed, focus main landmark, otherwise navigate forward or backward based on shift key.\n let handled = e.altKey ? this.focusMain() : this.navigate(e.target as Element, e.shiftKey);\n if (handled) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n }\n\n private focusMain() {\n let main = this.getLandmarkByRole('main');\n if (main && document.contains(main.ref.current)) {\n this.focusLandmark(main.ref.current, 'forward');\n return true;\n }\n\n return false;\n }\n\n private navigate(from: Element, backward: boolean) {\n let nextLandmark = this.getNextLandmark(from, {\n backward\n });\n\n if (!nextLandmark) {\n return false;\n }\n\n // If something was previously focused in the next landmark, then return focus to it\n if (nextLandmark.lastFocused) {\n let lastFocused = nextLandmark.lastFocused;\n if (document.body.contains(lastFocused)) {\n lastFocused.focus();\n return true;\n }\n }\n\n // Otherwise, focus the landmark itself\n if (document.contains(nextLandmark.ref.current)) {\n this.focusLandmark(nextLandmark.ref.current, backward ? 'backward' : 'forward');\n return true;\n }\n\n return false;\n }\n\n /**\n * Sets lastFocused for a landmark, if focus is moved within that landmark.\n * Lets the last focused landmark know it was blurred if something else is focused.\n */\n private focusinHandler(e: FocusEvent) {\n let currentLandmark = this.closestLandmark(e.target as Element);\n if (currentLandmark && currentLandmark.ref.current !== e.target) {\n this.updateLandmark({ref: currentLandmark.ref, lastFocused: e.target as FocusableElement});\n }\n let previousFocusedElement = e.relatedTarget as Element;\n if (previousFocusedElement) {\n let closestPreviousLandmark = this.closestLandmark(previousFocusedElement);\n if (closestPreviousLandmark && closestPreviousLandmark.ref.current === previousFocusedElement) {\n closestPreviousLandmark.blur();\n }\n }\n }\n\n /**\n * Track if the focus is lost to the body. If it is, do cleanup on the landmark that last had focus.\n */\n private focusoutHandler(e: FocusEvent) {\n let previousFocusedElement = e.target as Element;\n let nextFocusedElement = e.relatedTarget;\n // the === document seems to be a jest thing for focus to go there on generic blur event such as landmark.blur();\n // browsers appear to send focus instead to document.body and the relatedTarget is null when that happens\n if (!nextFocusedElement || nextFocusedElement === document) {\n let closestPreviousLandmark = this.closestLandmark(previousFocusedElement);\n if (closestPreviousLandmark && closestPreviousLandmark.ref.current === previousFocusedElement) {\n closestPreviousLandmark.blur();\n }\n }\n }\n\n public createLandmarkController(): LandmarkController {\n let instance = this;\n instance.refCount++;\n instance.setupIfNeeded();\n return {\n navigate(direction, opts) {\n return instance.navigate(opts?.from || document.activeElement, direction === 'backward');\n },\n focusNext(opts) {\n return instance.navigate(opts?.from || document.activeElement, false);\n },\n focusPrevious(opts) {\n return instance.navigate(opts?.from || document.activeElement, true);\n },\n focusMain() {\n return instance.focusMain();\n },\n dispose() {\n instance.refCount--;\n instance.teardownIfNeeded();\n instance = null;\n }\n };\n }\n\n public registerLandmark(landmark: Landmark): () => void {\n if (this.landmarks.find(l => l.ref === landmark.ref)) {\n this.updateLandmark(landmark);\n } else {\n this.addLandmark(landmark);\n }\n\n return () => this.removeLandmark(landmark.ref);\n }\n}\n\n/** Creates a LandmarkController, which allows programmatic navigation of landmarks. */\nexport function createLandmarkController(): LandmarkController {\n // Get the current landmark manager and create a controller using it.\n let instance = getLandmarkManager();\n let controller = instance.createLandmarkController();\n\n let unsubscribe = subscribe(() => {\n // If the landmark manager changes, dispose the old\n // controller and create a new one.\n controller.dispose();\n instance = getLandmarkManager();\n controller = instance.createLandmarkController();\n });\n\n // Return a wrapper that proxies requests to the current controller instance.\n return {\n navigate(direction, opts) {\n return controller.navigate(direction, opts);\n },\n focusNext(opts) {\n return controller.focusNext(opts);\n },\n focusPrevious(opts) {\n return controller.focusPrevious(opts);\n },\n focusMain() {\n return controller.focusMain();\n },\n dispose() {\n controller.dispose();\n unsubscribe();\n controller = null;\n instance = null;\n }\n };\n}\n\n/**\n * Provides landmark navigation in an application. Call this with a role and label to register a landmark navigable with F6.\n * @param props - Props for the landmark.\n * @param ref - Ref to the landmark.\n */\nexport function useLandmark(props: AriaLandmarkProps, ref: MutableRefObject<FocusableElement>): LandmarkAria {\n const {\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n focus\n } = props;\n let manager = useLandmarkManager();\n let label = ariaLabel || ariaLabelledby;\n let [isLandmarkFocused, setIsLandmarkFocused] = useState(false);\n\n let defaultFocus = useCallback(() => {\n setIsLandmarkFocused(true);\n }, [setIsLandmarkFocused]);\n\n let blur = useCallback(() => {\n setIsLandmarkFocused(false);\n }, [setIsLandmarkFocused]);\n\n useLayoutEffect(() => {\n return manager.registerLandmark({ref, label, role, focus: focus || defaultFocus, blur});\n }, [manager, label, ref, role, focus, defaultFocus, blur]);\n\n useEffect(() => {\n if (isLandmarkFocused) {\n ref.current.focus();\n }\n }, [isLandmarkFocused, ref]);\n\n return {\n landmarkProps: {\n role,\n tabIndex: isLandmarkFocused ? -1 : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby\n }\n };\n}\n"],"names":[],"version":3,"file":"module.js.map"}
|
|
1
|
+
{"mappings":"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","sources":["packages/@react-aria/landmark/src/index.ts","packages/@react-aria/landmark/src/useLandmark.ts"],"sourcesContent":["/*\n * Copyright 2022 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport type {AriaLandmarkRole, AriaLandmarkProps, LandmarkAria, LandmarkController} from './useLandmark';\nexport {useLandmark, createLandmarkController} from './useLandmark';\n","/*\n * Copyright 2022 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nimport {AriaLabelingProps, DOMAttributes, FocusableElement} from '@react-types/shared';\nimport {RefObject, useCallback, useEffect, useState} from 'react';\nimport {useLayoutEffect} from '@react-aria/utils';\nimport {useSyncExternalStore} from 'use-sync-external-store/shim/index.js';\n\nexport type AriaLandmarkRole = 'main' | 'region' | 'search' | 'navigation' | 'form' | 'banner' | 'contentinfo' | 'complementary';\n\nexport interface AriaLandmarkProps extends AriaLabelingProps {\n role: AriaLandmarkRole,\n focus?: (direction: 'forward' | 'backward') => void\n}\n\nexport interface LandmarkAria {\n landmarkProps: DOMAttributes\n}\n\n// Increment this version number whenever the\n// LandmarkManagerApi or Landmark interfaces change.\nconst LANDMARK_API_VERSION = 1;\n\n// Minimal API for LandmarkManager that must continue to work between versions.\n// Changes to this interface are considered breaking. New methods/properties are\n// safe to add, but changes or removals are not allowed (same as public APIs).\ninterface LandmarkManagerApi {\n version: number,\n createLandmarkController(): LandmarkController,\n registerLandmark(landmark: Landmark): () => void\n}\n\n// Changes to this interface are considered breaking.\n// New properties MUST be optional so that registering a landmark\n// from an older version of useLandmark against a newer version of\n// LandmarkManager does not crash.\ninterface Landmark {\n ref: RefObject<FocusableElement>,\n role: AriaLandmarkRole,\n label?: string,\n lastFocused?: FocusableElement,\n focus: (direction: 'forward' | 'backward') => void,\n blur: () => void\n}\n\nexport interface LandmarkControllerOptions {\n /**\n * The element from which to start navigating.\n * @default document.activeElement\n */\n from?: FocusableElement\n}\n\n/** A LandmarkController allows programmatic navigation of landmarks. */\nexport interface LandmarkController {\n /** Moves focus to the next landmark. */\n focusNext(opts?: LandmarkControllerOptions): boolean,\n /** Moves focus to the previous landmark. */\n focusPrevious(opts?: LandmarkControllerOptions): boolean,\n /** Moves focus to the main landmark. */\n focusMain(): boolean,\n /** Moves focus either forward or backward in the landmark sequence. */\n navigate(direction: 'forward' | 'backward', opts?: LandmarkControllerOptions): boolean,\n /**\n * Disposes the landmark controller. When no landmarks are registered, and no\n * controllers are active, the landmark keyboard listeners are removed from the page.\n */\n dispose(): void\n}\n\n// Symbol under which the singleton landmark manager instance is attached to the document.\nconst landmarkSymbol = Symbol.for('react-aria-landmark-manager');\n\nfunction subscribe(fn: () => void) {\n document.addEventListener('react-aria-landmark-manager-change', fn);\n return () => document.removeEventListener('react-aria-landmark-manager-change', fn);\n}\n\nfunction getLandmarkManager(): LandmarkManagerApi | null {\n if (typeof document === 'undefined') {\n return null;\n }\n\n // Reuse an existing instance if it has the same or greater version.\n let instance = document[landmarkSymbol];\n if (instance && instance.version >= LANDMARK_API_VERSION) {\n return instance;\n }\n\n // Otherwise, create a new instance and dispatch an event so anything using the existing\n // instance updates and re-registers their landmarks with the new one.\n document[landmarkSymbol] = new LandmarkManager();\n document.dispatchEvent(new CustomEvent('react-aria-landmark-manager-change'));\n return document[landmarkSymbol];\n}\n\n// Subscribes a React component to the current landmark manager instance.\nfunction useLandmarkManager(): LandmarkManagerApi | null {\n return useSyncExternalStore(subscribe, getLandmarkManager, getLandmarkManager);\n}\n\nclass LandmarkManager implements LandmarkManagerApi {\n private landmarks: Array<Landmark> = [];\n private isListening = false;\n private refCount = 0;\n public version = LANDMARK_API_VERSION;\n\n constructor() {\n this.f6Handler = this.f6Handler.bind(this);\n this.focusinHandler = this.focusinHandler.bind(this);\n this.focusoutHandler = this.focusoutHandler.bind(this);\n }\n\n private setupIfNeeded() {\n if (this.isListening) {\n return;\n }\n document.addEventListener('keydown', this.f6Handler, {capture: true});\n document.addEventListener('focusin', this.focusinHandler, {capture: true});\n document.addEventListener('focusout', this.focusoutHandler, {capture: true});\n this.isListening = true;\n }\n\n private teardownIfNeeded() {\n if (!this.isListening || this.landmarks.length > 0 || this.refCount > 0) {\n return;\n }\n document.removeEventListener('keydown', this.f6Handler, {capture: true});\n document.removeEventListener('focusin', this.focusinHandler, {capture: true});\n document.removeEventListener('focusout', this.focusoutHandler, {capture: true});\n this.isListening = false;\n }\n\n private focusLandmark(landmark: FocusableElement, direction: 'forward' | 'backward') {\n this.landmarks.find(l => l.ref.current === landmark)?.focus?.(direction);\n }\n\n /**\n * Return set of landmarks with a specific role.\n */\n private getLandmarksByRole(role: AriaLandmarkRole) {\n return new Set(this.landmarks.filter(l => l.role === role));\n }\n\n /**\n * Return first landmark with a specific role.\n */\n private getLandmarkByRole(role: AriaLandmarkRole) {\n return this.landmarks.find(l => l.role === role);\n }\n\n private addLandmark(newLandmark: Landmark) {\n this.setupIfNeeded();\n if (this.landmarks.find(landmark => landmark.ref === newLandmark.ref) || !newLandmark.ref.current) {\n return;\n }\n\n if (this.landmarks.filter(landmark => landmark.role === 'main').length > 1) {\n console.error('Page can contain no more than one landmark with the role \"main\".');\n }\n\n if (this.landmarks.length === 0) {\n this.landmarks = [newLandmark];\n this.checkLabels(newLandmark.role);\n return;\n }\n\n\n // Binary search to insert new landmark based on position in document relative to existing landmarks.\n // https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition\n let start = 0;\n let end = this.landmarks.length - 1;\n while (start <= end) {\n let mid = Math.floor((start + end) / 2);\n let comparedPosition = newLandmark.ref.current.compareDocumentPosition(this.landmarks[mid].ref.current as Node);\n let isNewAfterExisting = Boolean((comparedPosition & Node.DOCUMENT_POSITION_PRECEDING) || (comparedPosition & Node.DOCUMENT_POSITION_CONTAINS));\n\n if (isNewAfterExisting) {\n start = mid + 1;\n } else {\n end = mid - 1;\n }\n }\n\n this.landmarks.splice(start, 0, newLandmark);\n this.checkLabels(newLandmark.role);\n }\n\n private updateLandmark(landmark: Pick<Landmark, 'ref'> & Partial<Landmark>) {\n let index = this.landmarks.findIndex(l => l.ref === landmark.ref);\n if (index >= 0) {\n this.landmarks[index] = {...this.landmarks[index], ...landmark};\n this.checkLabels(this.landmarks[index].role);\n }\n }\n\n private removeLandmark(ref: RefObject<Element>) {\n this.landmarks = this.landmarks.filter(landmark => landmark.ref !== ref);\n this.teardownIfNeeded();\n }\n\n /**\n * Warn if there are 2+ landmarks with the same role but no label.\n * Labels for landmarks with the same role must also be unique.\n *\n * See https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/.\n */\n private checkLabels(role: AriaLandmarkRole) {\n let landmarksWithRole = this.getLandmarksByRole(role);\n if (landmarksWithRole.size > 1) {\n let duplicatesWithoutLabel = [...landmarksWithRole].filter(landmark => !landmark.label);\n if (duplicatesWithoutLabel.length > 0) {\n console.warn(\n `Page contains more than one landmark with the '${role}' role. If two or more landmarks on a page share the same role, all must be labeled with an aria-label or aria-labelledby attribute: `,\n duplicatesWithoutLabel.map(landmark => landmark.ref.current)\n );\n } else {\n let labels = [...landmarksWithRole].map(landmark => landmark.label);\n let duplicateLabels = labels.filter((item, index) => labels.indexOf(item) !== index);\n\n duplicateLabels.forEach((label) => {\n console.warn(\n `Page contains more than one landmark with the '${role}' role and '${label}' label. If two or more landmarks on a page share the same role, they must have unique labels: `,\n [...landmarksWithRole].filter(landmark => landmark.label === label).map(landmark => landmark.ref.current)\n );\n });\n }\n }\n }\n\n /**\n * Get the landmark that is the closest parent in the DOM.\n * Returns undefined if no parent is a landmark.\n */\n private closestLandmark(element: FocusableElement) {\n let landmarkMap = new Map(this.landmarks.map(l => [l.ref.current, l]));\n let currentElement = element;\n while (currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body && currentElement.parentElement) {\n currentElement = currentElement.parentElement;\n }\n return landmarkMap.get(currentElement);\n }\n\n /**\n * Gets the next landmark, in DOM focus order, or previous if backwards is specified.\n * If last landmark, next should be the first landmark.\n * If not inside a landmark, will return first landmark.\n * Returns undefined if there are no landmarks.\n */\n private getNextLandmark(element: FocusableElement, {backward}: {backward?: boolean }) {\n let currentLandmark = this.closestLandmark(element);\n let nextLandmarkIndex = backward ? this.landmarks.length - 1 : 0;\n if (currentLandmark) {\n nextLandmarkIndex = this.landmarks.indexOf(currentLandmark) + (backward ? -1 : 1);\n }\n\n let wrapIfNeeded = () => {\n // When we reach the end of the landmark sequence, fire a custom event that can be listened for by applications.\n // If this event is canceled, we return immediately. This can be used to implement landmark navigation across iframes.\n if (nextLandmarkIndex < 0) {\n if (!element.dispatchEvent(new CustomEvent('react-aria-landmark-navigation', {detail: {direction: 'backward'}, bubbles: true, cancelable: true}))) {\n return true;\n }\n\n nextLandmarkIndex = this.landmarks.length - 1;\n } else if (nextLandmarkIndex >= this.landmarks.length) {\n if (!element.dispatchEvent(new CustomEvent('react-aria-landmark-navigation', {detail: {direction: 'forward'}, bubbles: true, cancelable: true}))) {\n return true;\n }\n\n nextLandmarkIndex = 0;\n }\n\n if (nextLandmarkIndex < 0 || nextLandmarkIndex >= this.landmarks.length) {\n return true;\n }\n\n return false;\n };\n\n if (wrapIfNeeded()) {\n return undefined;\n }\n\n // Skip over hidden landmarks.\n let i = nextLandmarkIndex;\n while (this.landmarks[nextLandmarkIndex].ref.current?.closest('[aria-hidden=true]')) {\n nextLandmarkIndex += backward ? -1 : 1;\n if (wrapIfNeeded()) {\n return undefined;\n }\n\n if (nextLandmarkIndex === i) {\n break;\n }\n }\n\n return this.landmarks[nextLandmarkIndex];\n }\n\n /**\n * Look at next landmark. If an element was previously focused inside, restore focus there.\n * If not, focus the landmark itself.\n * If no landmarks at all, or none with focusable elements, don't move focus.\n */\n private f6Handler(e: KeyboardEvent) {\n if (e.key === 'F6') {\n // If alt key pressed, focus main landmark, otherwise navigate forward or backward based on shift key.\n let handled = e.altKey ? this.focusMain() : this.navigate(e.target as FocusableElement, e.shiftKey);\n if (handled) {\n e.preventDefault();\n e.stopPropagation();\n }\n }\n }\n\n private focusMain() {\n let main = this.getLandmarkByRole('main');\n if (main && main.ref.current && document.contains(main.ref.current)) {\n this.focusLandmark(main.ref.current, 'forward');\n return true;\n }\n\n return false;\n }\n\n private navigate(from: FocusableElement, backward: boolean) {\n let nextLandmark = this.getNextLandmark(from, {\n backward\n });\n\n if (!nextLandmark) {\n return false;\n }\n\n // If something was previously focused in the next landmark, then return focus to it\n if (nextLandmark.lastFocused) {\n let lastFocused = nextLandmark.lastFocused;\n if (document.body.contains(lastFocused)) {\n lastFocused.focus();\n return true;\n }\n }\n\n // Otherwise, focus the landmark itself\n if (nextLandmark.ref.current && document.contains(nextLandmark.ref.current)) {\n this.focusLandmark(nextLandmark.ref.current, backward ? 'backward' : 'forward');\n return true;\n }\n\n return false;\n }\n\n /**\n * Sets lastFocused for a landmark, if focus is moved within that landmark.\n * Lets the last focused landmark know it was blurred if something else is focused.\n */\n private focusinHandler(e: FocusEvent) {\n let currentLandmark = this.closestLandmark(e.target as FocusableElement);\n if (currentLandmark && currentLandmark.ref.current !== e.target) {\n this.updateLandmark({ref: currentLandmark.ref, lastFocused: e.target as FocusableElement});\n }\n let previousFocusedElement = e.relatedTarget as FocusableElement;\n if (previousFocusedElement) {\n let closestPreviousLandmark = this.closestLandmark(previousFocusedElement);\n if (closestPreviousLandmark && closestPreviousLandmark.ref.current === previousFocusedElement) {\n closestPreviousLandmark.blur();\n }\n }\n }\n\n /**\n * Track if the focus is lost to the body. If it is, do cleanup on the landmark that last had focus.\n */\n private focusoutHandler(e: FocusEvent) {\n let previousFocusedElement = e.target as FocusableElement;\n let nextFocusedElement = e.relatedTarget;\n // the === document seems to be a jest thing for focus to go there on generic blur event such as landmark.blur();\n // browsers appear to send focus instead to document.body and the relatedTarget is null when that happens\n if (!nextFocusedElement || nextFocusedElement === document) {\n let closestPreviousLandmark = this.closestLandmark(previousFocusedElement);\n if (closestPreviousLandmark && closestPreviousLandmark.ref.current === previousFocusedElement) {\n closestPreviousLandmark.blur();\n }\n }\n }\n\n public createLandmarkController(): LandmarkController {\n let instance: LandmarkManager | null = this;\n instance.refCount++;\n instance.setupIfNeeded();\n return {\n navigate(direction, opts) {\n let element = opts?.from || (document!.activeElement as FocusableElement);\n return instance!.navigate(element, direction === 'backward');\n },\n focusNext(opts) {\n let element = opts?.from || (document!.activeElement as FocusableElement);\n return instance!.navigate(element, false);\n },\n focusPrevious(opts) {\n let element = opts?.from || (document!.activeElement as FocusableElement);\n return instance!.navigate(element, true);\n },\n focusMain() {\n return instance!.focusMain();\n },\n dispose() {\n if (instance) {\n instance.refCount--;\n instance.teardownIfNeeded();\n instance = null;\n }\n }\n };\n }\n\n public registerLandmark(landmark: Landmark): () => void {\n if (this.landmarks.find(l => l.ref === landmark.ref)) {\n this.updateLandmark(landmark);\n } else {\n this.addLandmark(landmark);\n }\n\n return () => this.removeLandmark(landmark.ref);\n }\n}\n\n/** Creates a LandmarkController, which allows programmatic navigation of landmarks. */\nexport function createLandmarkController(): LandmarkController {\n // Get the current landmark manager and create a controller using it.\n let instance: LandmarkManagerApi | null = getLandmarkManager();\n let controller = instance?.createLandmarkController();\n\n let unsubscribe = subscribe(() => {\n // If the landmark manager changes, dispose the old\n // controller and create a new one.\n controller?.dispose();\n instance = getLandmarkManager();\n controller = instance?.createLandmarkController();\n });\n\n // Return a wrapper that proxies requests to the current controller instance.\n return {\n navigate(direction, opts) {\n return controller!.navigate(direction, opts);\n },\n focusNext(opts) {\n return controller!.focusNext(opts);\n },\n focusPrevious(opts) {\n return controller!.focusPrevious(opts);\n },\n focusMain() {\n return controller!.focusMain();\n },\n dispose() {\n controller?.dispose();\n unsubscribe();\n controller = undefined;\n instance = null;\n }\n };\n}\n\n/**\n * Provides landmark navigation in an application. Call this with a role and label to register a landmark navigable with F6.\n * @param props - Props for the landmark.\n * @param ref - Ref to the landmark.\n */\nexport function useLandmark(props: AriaLandmarkProps, ref: RefObject<FocusableElement>): LandmarkAria {\n const {\n role,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby,\n focus\n } = props;\n let manager = useLandmarkManager();\n let label = ariaLabel || ariaLabelledby;\n let [isLandmarkFocused, setIsLandmarkFocused] = useState(false);\n\n let defaultFocus = useCallback(() => {\n setIsLandmarkFocused(true);\n }, [setIsLandmarkFocused]);\n\n let blur = useCallback(() => {\n setIsLandmarkFocused(false);\n }, [setIsLandmarkFocused]);\n\n useLayoutEffect(() => {\n if (manager) {\n return manager.registerLandmark({ref, label, role, focus: focus || defaultFocus, blur});\n }\n }, [manager, label, ref, role, focus, defaultFocus, blur]);\n\n useEffect(() => {\n if (isLandmarkFocused) {\n ref.current?.focus();\n }\n }, [isLandmarkFocused, ref]);\n\n return {\n landmarkProps: {\n role,\n tabIndex: isLandmarkFocused ? -1 : undefined,\n 'aria-label': ariaLabel,\n 'aria-labelledby': ariaLabelledby\n }\n };\n}\n"],"names":[],"version":3,"file":"module.js.map"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { AriaLabelingProps, DOMAttributes, FocusableElement } from "@react-types/shared";
|
|
2
|
-
import {
|
|
2
|
+
import { RefObject } from "react";
|
|
3
3
|
export type AriaLandmarkRole = 'main' | 'region' | 'search' | 'navigation' | 'form' | 'banner' | 'contentinfo' | 'complementary';
|
|
4
4
|
export interface AriaLandmarkProps extends AriaLabelingProps {
|
|
5
5
|
role: AriaLandmarkRole;
|
|
@@ -13,7 +13,7 @@ interface LandmarkControllerOptions {
|
|
|
13
13
|
* The element from which to start navigating.
|
|
14
14
|
* @default document.activeElement
|
|
15
15
|
*/
|
|
16
|
-
from?:
|
|
16
|
+
from?: FocusableElement;
|
|
17
17
|
}
|
|
18
18
|
/** A LandmarkController allows programmatic navigation of landmarks. */
|
|
19
19
|
export interface LandmarkController {
|
|
@@ -38,6 +38,6 @@ export function createLandmarkController(): LandmarkController;
|
|
|
38
38
|
* @param props - Props for the landmark.
|
|
39
39
|
* @param ref - Ref to the landmark.
|
|
40
40
|
*/
|
|
41
|
-
export function useLandmark(props: AriaLandmarkProps, ref:
|
|
41
|
+
export function useLandmark(props: AriaLandmarkProps, ref: RefObject<FocusableElement>): LandmarkAria;
|
|
42
42
|
|
|
43
43
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":";;AAiBA,+BAA+B,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,aAAa,GAAG,eAAe,CAAC;AAEjI,kCAAmC,SAAQ,iBAAiB;IAC1D,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,GAAG,UAAU,KAAK,IAAI,CAAA;CACpD;AAED;IACE,aAAa,EAAE,aAAa,CAAA;CAC7B;AA4BD;IACE;;;OAGG;IACH,IAAI,CAAC,EAAE,
|
|
1
|
+
{"mappings":";;AAiBA,+BAA+B,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,aAAa,GAAG,eAAe,CAAC;AAEjI,kCAAmC,SAAQ,iBAAiB;IAC1D,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,GAAG,UAAU,KAAK,IAAI,CAAA;CACpD;AAED;IACE,aAAa,EAAE,aAAa,CAAA;CAC7B;AA4BD;IACE;;;OAGG;IACH,IAAI,CAAC,EAAE,gBAAgB,CAAA;CACxB;AAED,wEAAwE;AACxE;IACE,wCAAwC;IACxC,SAAS,CAAC,IAAI,CAAC,EAAE,yBAAyB,GAAG,OAAO,CAAC;IACrD,4CAA4C;IAC5C,aAAa,CAAC,IAAI,CAAC,EAAE,yBAAyB,GAAG,OAAO,CAAC;IACzD,wCAAwC;IACxC,SAAS,IAAI,OAAO,CAAC;IACrB,uEAAuE;IACvE,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,UAAU,EAAE,IAAI,CAAC,EAAE,yBAAyB,GAAG,OAAO,CAAC;IACvF;;;OAGG;IACH,OAAO,IAAI,IAAI,CAAA;CAChB;AAwWD,uFAAuF;AACvF,4CAA4C,kBAAkB,CAkC7D;AAED;;;;GAIG;AACH,4BAA4B,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,UAAU,gBAAgB,CAAC,GAAG,YAAY,CAuCpG","sources":["packages/@react-aria/landmark/src/packages/@react-aria/landmark/src/useLandmark.ts","packages/@react-aria/landmark/src/packages/@react-aria/landmark/src/index.ts","packages/@react-aria/landmark/src/index.ts"],"sourcesContent":[null,null,"/*\n * Copyright 2022 Adobe. All rights reserved.\n * This file is licensed to you under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License. You may obtain a copy\n * of the License at http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under\n * the License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS\n * OF ANY KIND, either express or implied. See the License for the specific language\n * governing permissions and limitations under the License.\n */\n\nexport type {AriaLandmarkRole, AriaLandmarkProps, LandmarkAria, LandmarkController} from './useLandmark';\nexport {useLandmark, createLandmarkController} from './useLandmark';\n"],"names":[],"version":3,"file":"types.d.ts.map"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@react-aria/landmark",
|
|
3
|
-
"version": "3.0.0-nightly.
|
|
3
|
+
"version": "3.0.0-nightly.3760+bd5adc8a8",
|
|
4
4
|
"description": "Spectrum UI components in React",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/main.js",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"url": "https://github.com/adobe/react-spectrum"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@react-aria/focus": "3.0.0-nightly.
|
|
26
|
-
"@react-aria/utils": "3.0.0-nightly.
|
|
27
|
-
"@react-types/shared": "3.0.0-nightly.
|
|
25
|
+
"@react-aria/focus": "3.0.0-nightly.2060+bd5adc8a8",
|
|
26
|
+
"@react-aria/utils": "3.0.0-nightly.2060+bd5adc8a8",
|
|
27
|
+
"@react-types/shared": "3.0.0-nightly.2060+bd5adc8a8",
|
|
28
28
|
"@swc/helpers": "^0.4.14",
|
|
29
29
|
"use-sync-external-store": "^1.2.0"
|
|
30
30
|
},
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"publishConfig": {
|
|
35
35
|
"access": "public"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "bd5adc8a81623b6727fdc0521f47f9baa04517bb"
|
|
38
38
|
}
|
package/src/useLandmark.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import {AriaLabelingProps, DOMAttributes, FocusableElement} from '@react-types/shared';
|
|
14
|
-
import {
|
|
14
|
+
import {RefObject, useCallback, useEffect, useState} from 'react';
|
|
15
15
|
import {useLayoutEffect} from '@react-aria/utils';
|
|
16
16
|
import {useSyncExternalStore} from 'use-sync-external-store/shim/index.js';
|
|
17
17
|
|
|
@@ -44,7 +44,7 @@ interface LandmarkManagerApi {
|
|
|
44
44
|
// from an older version of useLandmark against a newer version of
|
|
45
45
|
// LandmarkManager does not crash.
|
|
46
46
|
interface Landmark {
|
|
47
|
-
ref:
|
|
47
|
+
ref: RefObject<FocusableElement>,
|
|
48
48
|
role: AriaLandmarkRole,
|
|
49
49
|
label?: string,
|
|
50
50
|
lastFocused?: FocusableElement,
|
|
@@ -57,7 +57,7 @@ export interface LandmarkControllerOptions {
|
|
|
57
57
|
* The element from which to start navigating.
|
|
58
58
|
* @default document.activeElement
|
|
59
59
|
*/
|
|
60
|
-
from?:
|
|
60
|
+
from?: FocusableElement
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/** A LandmarkController allows programmatic navigation of landmarks. */
|
|
@@ -140,8 +140,8 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
140
140
|
this.isListening = false;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
private focusLandmark(landmark:
|
|
144
|
-
this.landmarks.find(l => l.ref.current === landmark)?.focus(direction);
|
|
143
|
+
private focusLandmark(landmark: FocusableElement, direction: 'forward' | 'backward') {
|
|
144
|
+
this.landmarks.find(l => l.ref.current === landmark)?.focus?.(direction);
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
/**
|
|
@@ -160,7 +160,7 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
160
160
|
|
|
161
161
|
private addLandmark(newLandmark: Landmark) {
|
|
162
162
|
this.setupIfNeeded();
|
|
163
|
-
if (this.landmarks.find(landmark => landmark.ref === newLandmark.ref)) {
|
|
163
|
+
if (this.landmarks.find(landmark => landmark.ref === newLandmark.ref) || !newLandmark.ref.current) {
|
|
164
164
|
return;
|
|
165
165
|
}
|
|
166
166
|
|
|
@@ -203,7 +203,7 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
203
203
|
}
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
private removeLandmark(ref:
|
|
206
|
+
private removeLandmark(ref: RefObject<Element>) {
|
|
207
207
|
this.landmarks = this.landmarks.filter(landmark => landmark.ref !== ref);
|
|
208
208
|
this.teardownIfNeeded();
|
|
209
209
|
}
|
|
@@ -241,10 +241,10 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
241
241
|
* Get the landmark that is the closest parent in the DOM.
|
|
242
242
|
* Returns undefined if no parent is a landmark.
|
|
243
243
|
*/
|
|
244
|
-
private closestLandmark(element:
|
|
244
|
+
private closestLandmark(element: FocusableElement) {
|
|
245
245
|
let landmarkMap = new Map(this.landmarks.map(l => [l.ref.current, l]));
|
|
246
246
|
let currentElement = element;
|
|
247
|
-
while (currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body) {
|
|
247
|
+
while (currentElement && !landmarkMap.has(currentElement) && currentElement !== document.body && currentElement.parentElement) {
|
|
248
248
|
currentElement = currentElement.parentElement;
|
|
249
249
|
}
|
|
250
250
|
return landmarkMap.get(currentElement);
|
|
@@ -256,7 +256,7 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
256
256
|
* If not inside a landmark, will return first landmark.
|
|
257
257
|
* Returns undefined if there are no landmarks.
|
|
258
258
|
*/
|
|
259
|
-
private getNextLandmark(element:
|
|
259
|
+
private getNextLandmark(element: FocusableElement, {backward}: {backward?: boolean }) {
|
|
260
260
|
let currentLandmark = this.closestLandmark(element);
|
|
261
261
|
let nextLandmarkIndex = backward ? this.landmarks.length - 1 : 0;
|
|
262
262
|
if (currentLandmark) {
|
|
@@ -293,7 +293,7 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
293
293
|
|
|
294
294
|
// Skip over hidden landmarks.
|
|
295
295
|
let i = nextLandmarkIndex;
|
|
296
|
-
while (this.landmarks[nextLandmarkIndex].ref.current
|
|
296
|
+
while (this.landmarks[nextLandmarkIndex].ref.current?.closest('[aria-hidden=true]')) {
|
|
297
297
|
nextLandmarkIndex += backward ? -1 : 1;
|
|
298
298
|
if (wrapIfNeeded()) {
|
|
299
299
|
return undefined;
|
|
@@ -315,7 +315,7 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
315
315
|
private f6Handler(e: KeyboardEvent) {
|
|
316
316
|
if (e.key === 'F6') {
|
|
317
317
|
// If alt key pressed, focus main landmark, otherwise navigate forward or backward based on shift key.
|
|
318
|
-
let handled = e.altKey ? this.focusMain() : this.navigate(e.target as
|
|
318
|
+
let handled = e.altKey ? this.focusMain() : this.navigate(e.target as FocusableElement, e.shiftKey);
|
|
319
319
|
if (handled) {
|
|
320
320
|
e.preventDefault();
|
|
321
321
|
e.stopPropagation();
|
|
@@ -325,7 +325,7 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
325
325
|
|
|
326
326
|
private focusMain() {
|
|
327
327
|
let main = this.getLandmarkByRole('main');
|
|
328
|
-
if (main && document.contains(main.ref.current)) {
|
|
328
|
+
if (main && main.ref.current && document.contains(main.ref.current)) {
|
|
329
329
|
this.focusLandmark(main.ref.current, 'forward');
|
|
330
330
|
return true;
|
|
331
331
|
}
|
|
@@ -333,7 +333,7 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
333
333
|
return false;
|
|
334
334
|
}
|
|
335
335
|
|
|
336
|
-
private navigate(from:
|
|
336
|
+
private navigate(from: FocusableElement, backward: boolean) {
|
|
337
337
|
let nextLandmark = this.getNextLandmark(from, {
|
|
338
338
|
backward
|
|
339
339
|
});
|
|
@@ -352,7 +352,7 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
// Otherwise, focus the landmark itself
|
|
355
|
-
if (document.contains(nextLandmark.ref.current)) {
|
|
355
|
+
if (nextLandmark.ref.current && document.contains(nextLandmark.ref.current)) {
|
|
356
356
|
this.focusLandmark(nextLandmark.ref.current, backward ? 'backward' : 'forward');
|
|
357
357
|
return true;
|
|
358
358
|
}
|
|
@@ -365,11 +365,11 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
365
365
|
* Lets the last focused landmark know it was blurred if something else is focused.
|
|
366
366
|
*/
|
|
367
367
|
private focusinHandler(e: FocusEvent) {
|
|
368
|
-
let currentLandmark = this.closestLandmark(e.target as
|
|
368
|
+
let currentLandmark = this.closestLandmark(e.target as FocusableElement);
|
|
369
369
|
if (currentLandmark && currentLandmark.ref.current !== e.target) {
|
|
370
370
|
this.updateLandmark({ref: currentLandmark.ref, lastFocused: e.target as FocusableElement});
|
|
371
371
|
}
|
|
372
|
-
let previousFocusedElement = e.relatedTarget as
|
|
372
|
+
let previousFocusedElement = e.relatedTarget as FocusableElement;
|
|
373
373
|
if (previousFocusedElement) {
|
|
374
374
|
let closestPreviousLandmark = this.closestLandmark(previousFocusedElement);
|
|
375
375
|
if (closestPreviousLandmark && closestPreviousLandmark.ref.current === previousFocusedElement) {
|
|
@@ -382,7 +382,7 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
382
382
|
* Track if the focus is lost to the body. If it is, do cleanup on the landmark that last had focus.
|
|
383
383
|
*/
|
|
384
384
|
private focusoutHandler(e: FocusEvent) {
|
|
385
|
-
let previousFocusedElement = e.target as
|
|
385
|
+
let previousFocusedElement = e.target as FocusableElement;
|
|
386
386
|
let nextFocusedElement = e.relatedTarget;
|
|
387
387
|
// the === document seems to be a jest thing for focus to go there on generic blur event such as landmark.blur();
|
|
388
388
|
// browsers appear to send focus instead to document.body and the relatedTarget is null when that happens
|
|
@@ -395,26 +395,31 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
395
395
|
}
|
|
396
396
|
|
|
397
397
|
public createLandmarkController(): LandmarkController {
|
|
398
|
-
let instance = this;
|
|
398
|
+
let instance: LandmarkManager | null = this;
|
|
399
399
|
instance.refCount++;
|
|
400
400
|
instance.setupIfNeeded();
|
|
401
401
|
return {
|
|
402
402
|
navigate(direction, opts) {
|
|
403
|
-
|
|
403
|
+
let element = opts?.from || (document!.activeElement as FocusableElement);
|
|
404
|
+
return instance!.navigate(element, direction === 'backward');
|
|
404
405
|
},
|
|
405
406
|
focusNext(opts) {
|
|
406
|
-
|
|
407
|
+
let element = opts?.from || (document!.activeElement as FocusableElement);
|
|
408
|
+
return instance!.navigate(element, false);
|
|
407
409
|
},
|
|
408
410
|
focusPrevious(opts) {
|
|
409
|
-
|
|
411
|
+
let element = opts?.from || (document!.activeElement as FocusableElement);
|
|
412
|
+
return instance!.navigate(element, true);
|
|
410
413
|
},
|
|
411
414
|
focusMain() {
|
|
412
|
-
return instance
|
|
415
|
+
return instance!.focusMain();
|
|
413
416
|
},
|
|
414
417
|
dispose() {
|
|
415
|
-
instance
|
|
416
|
-
|
|
417
|
-
|
|
418
|
+
if (instance) {
|
|
419
|
+
instance.refCount--;
|
|
420
|
+
instance.teardownIfNeeded();
|
|
421
|
+
instance = null;
|
|
422
|
+
}
|
|
418
423
|
}
|
|
419
424
|
};
|
|
420
425
|
}
|
|
@@ -433,35 +438,35 @@ class LandmarkManager implements LandmarkManagerApi {
|
|
|
433
438
|
/** Creates a LandmarkController, which allows programmatic navigation of landmarks. */
|
|
434
439
|
export function createLandmarkController(): LandmarkController {
|
|
435
440
|
// Get the current landmark manager and create a controller using it.
|
|
436
|
-
let instance = getLandmarkManager();
|
|
437
|
-
let controller = instance
|
|
441
|
+
let instance: LandmarkManagerApi | null = getLandmarkManager();
|
|
442
|
+
let controller = instance?.createLandmarkController();
|
|
438
443
|
|
|
439
444
|
let unsubscribe = subscribe(() => {
|
|
440
445
|
// If the landmark manager changes, dispose the old
|
|
441
446
|
// controller and create a new one.
|
|
442
|
-
controller
|
|
447
|
+
controller?.dispose();
|
|
443
448
|
instance = getLandmarkManager();
|
|
444
|
-
controller = instance
|
|
449
|
+
controller = instance?.createLandmarkController();
|
|
445
450
|
});
|
|
446
451
|
|
|
447
452
|
// Return a wrapper that proxies requests to the current controller instance.
|
|
448
453
|
return {
|
|
449
454
|
navigate(direction, opts) {
|
|
450
|
-
return controller
|
|
455
|
+
return controller!.navigate(direction, opts);
|
|
451
456
|
},
|
|
452
457
|
focusNext(opts) {
|
|
453
|
-
return controller
|
|
458
|
+
return controller!.focusNext(opts);
|
|
454
459
|
},
|
|
455
460
|
focusPrevious(opts) {
|
|
456
|
-
return controller
|
|
461
|
+
return controller!.focusPrevious(opts);
|
|
457
462
|
},
|
|
458
463
|
focusMain() {
|
|
459
|
-
return controller
|
|
464
|
+
return controller!.focusMain();
|
|
460
465
|
},
|
|
461
466
|
dispose() {
|
|
462
|
-
controller
|
|
467
|
+
controller?.dispose();
|
|
463
468
|
unsubscribe();
|
|
464
|
-
controller =
|
|
469
|
+
controller = undefined;
|
|
465
470
|
instance = null;
|
|
466
471
|
}
|
|
467
472
|
};
|
|
@@ -472,7 +477,7 @@ export function createLandmarkController(): LandmarkController {
|
|
|
472
477
|
* @param props - Props for the landmark.
|
|
473
478
|
* @param ref - Ref to the landmark.
|
|
474
479
|
*/
|
|
475
|
-
export function useLandmark(props: AriaLandmarkProps, ref:
|
|
480
|
+
export function useLandmark(props: AriaLandmarkProps, ref: RefObject<FocusableElement>): LandmarkAria {
|
|
476
481
|
const {
|
|
477
482
|
role,
|
|
478
483
|
'aria-label': ariaLabel,
|
|
@@ -492,12 +497,14 @@ export function useLandmark(props: AriaLandmarkProps, ref: MutableRefObject<Focu
|
|
|
492
497
|
}, [setIsLandmarkFocused]);
|
|
493
498
|
|
|
494
499
|
useLayoutEffect(() => {
|
|
495
|
-
|
|
500
|
+
if (manager) {
|
|
501
|
+
return manager.registerLandmark({ref, label, role, focus: focus || defaultFocus, blur});
|
|
502
|
+
}
|
|
496
503
|
}, [manager, label, ref, role, focus, defaultFocus, blur]);
|
|
497
504
|
|
|
498
505
|
useEffect(() => {
|
|
499
506
|
if (isLandmarkFocused) {
|
|
500
|
-
ref.current
|
|
507
|
+
ref.current?.focus();
|
|
501
508
|
}
|
|
502
509
|
}, [isLandmarkFocused, ref]);
|
|
503
510
|
|