@atlaskit/top-layer 0.12.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cjs/entry-points/focus.js +6 -4
  3. package/dist/cjs/entry-points/use-anchor-position-at-point.js +12 -0
  4. package/dist/cjs/focus/get-first-focusable.js +19 -0
  5. package/dist/cjs/focus/get-focusables.js +30 -0
  6. package/dist/cjs/focus/get-last-focusable.js +19 -0
  7. package/dist/cjs/focus/get-next-focusable.js +43 -0
  8. package/dist/cjs/focus/is-in-same-layer.js +23 -0
  9. package/dist/cjs/focus/is-nested-layer-focused.js +32 -0
  10. package/dist/cjs/focus/selectors.js +19 -0
  11. package/dist/cjs/focus/types.js +1 -0
  12. package/dist/cjs/internal/role-to-aria-has-popup.js +37 -0
  13. package/dist/cjs/internal/role-types.js +1 -97
  14. package/dist/cjs/internal/should-focus-into-popover.js +19 -0
  15. package/dist/cjs/internal/use-anchor-position-at-point.js +127 -0
  16. package/dist/cjs/internal/use-anchor-position.js +7 -3
  17. package/dist/cjs/internal/use-focus-wrap.js +8 -5
  18. package/dist/cjs/internal/use-initial-focus.js +4 -4
  19. package/dist/cjs/placement-map/index.js +2 -2
  20. package/dist/cjs/placement-map/legacy-placements.js +2 -101
  21. package/dist/cjs/placement-map/placement-mapping.js +103 -0
  22. package/dist/cjs/popup/popup-content.js +4 -3
  23. package/dist/cjs/use-arrow-navigation/use-arrow-navigation.js +7 -5
  24. package/dist/es2019/entry-points/focus.js +3 -1
  25. package/dist/es2019/entry-points/use-anchor-position-at-point.js +1 -0
  26. package/dist/es2019/focus/get-first-focusable.js +14 -0
  27. package/dist/es2019/focus/get-focusables.js +19 -0
  28. package/dist/es2019/focus/get-last-focusable.js +14 -0
  29. package/dist/es2019/focus/get-next-focusable.js +38 -0
  30. package/dist/es2019/focus/is-in-same-layer.js +18 -0
  31. package/dist/es2019/focus/is-nested-layer-focused.js +28 -0
  32. package/dist/es2019/focus/selectors.js +11 -0
  33. package/dist/es2019/focus/types.js +0 -0
  34. package/dist/es2019/internal/role-to-aria-has-popup.js +32 -0
  35. package/dist/es2019/internal/role-types.js +0 -92
  36. package/dist/es2019/internal/should-focus-into-popover.js +14 -0
  37. package/dist/es2019/internal/use-anchor-position-at-point.js +122 -0
  38. package/dist/es2019/internal/use-anchor-position.js +6 -3
  39. package/dist/es2019/internal/use-focus-wrap.js +4 -1
  40. package/dist/es2019/internal/use-initial-focus.js +1 -1
  41. package/dist/es2019/placement-map/index.js +1 -1
  42. package/dist/es2019/placement-map/legacy-placements.js +1 -100
  43. package/dist/es2019/placement-map/placement-mapping.js +97 -0
  44. package/dist/es2019/popup/popup-content.js +2 -1
  45. package/dist/es2019/use-arrow-navigation/use-arrow-navigation.js +3 -1
  46. package/dist/esm/entry-points/focus.js +3 -1
  47. package/dist/esm/entry-points/use-anchor-position-at-point.js +1 -0
  48. package/dist/esm/focus/get-first-focusable.js +13 -0
  49. package/dist/esm/focus/get-focusables.js +24 -0
  50. package/dist/esm/focus/get-last-focusable.js +13 -0
  51. package/dist/esm/focus/get-next-focusable.js +37 -0
  52. package/dist/esm/focus/is-in-same-layer.js +17 -0
  53. package/dist/esm/focus/is-nested-layer-focused.js +27 -0
  54. package/dist/esm/focus/selectors.js +13 -0
  55. package/dist/esm/focus/types.js +0 -0
  56. package/dist/esm/internal/role-to-aria-has-popup.js +31 -0
  57. package/dist/esm/internal/role-types.js +0 -90
  58. package/dist/esm/internal/should-focus-into-popover.js +13 -0
  59. package/dist/esm/internal/use-anchor-position-at-point.js +122 -0
  60. package/dist/esm/internal/use-anchor-position.js +7 -3
  61. package/dist/esm/internal/use-focus-wrap.js +4 -1
  62. package/dist/esm/internal/use-initial-focus.js +1 -1
  63. package/dist/esm/placement-map/index.js +1 -1
  64. package/dist/esm/placement-map/legacy-placements.js +1 -100
  65. package/dist/esm/placement-map/placement-mapping.js +97 -0
  66. package/dist/esm/popup/popup-content.js +2 -1
  67. package/dist/esm/use-arrow-navigation/use-arrow-navigation.js +3 -1
  68. package/dist/types/entry-points/focus.d.ts +4 -2
  69. package/dist/types/entry-points/use-anchor-position-at-point.d.ts +1 -0
  70. package/dist/types/focus/get-first-focusable.d.ts +8 -0
  71. package/dist/types/focus/get-focusables.d.ts +13 -0
  72. package/dist/types/focus/get-last-focusable.d.ts +8 -0
  73. package/dist/types/focus/get-next-focusable.d.ts +13 -0
  74. package/dist/types/focus/is-in-same-layer.d.ts +8 -0
  75. package/dist/types/focus/is-nested-layer-focused.d.ts +8 -0
  76. package/dist/types/focus/selectors.d.ts +4 -0
  77. package/dist/types/focus/types.d.ts +5 -0
  78. package/dist/types/internal/role-to-aria-has-popup.d.ts +13 -0
  79. package/dist/types/internal/role-types.d.ts +0 -19
  80. package/dist/types/internal/should-focus-into-popover.d.ts +8 -0
  81. package/dist/types/internal/use-anchor-position-at-point.d.ts +62 -0
  82. package/dist/types/internal/use-anchor-position.d.ts +6 -1
  83. package/dist/types/placement-map/index.d.ts +1 -1
  84. package/dist/types/placement-map/legacy-placements.d.ts +0 -12
  85. package/dist/types/placement-map/placement-mapping.d.ts +13 -0
  86. package/dist/types/use-arrow-navigation/use-arrow-navigation.d.ts +1 -1
  87. package/dist/types-ts4.5/entry-points/focus.d.ts +4 -2
  88. package/dist/types-ts4.5/entry-points/use-anchor-position-at-point.d.ts +1 -0
  89. package/dist/types-ts4.5/focus/get-first-focusable.d.ts +8 -0
  90. package/dist/types-ts4.5/focus/get-focusables.d.ts +13 -0
  91. package/dist/types-ts4.5/focus/get-last-focusable.d.ts +8 -0
  92. package/dist/types-ts4.5/focus/get-next-focusable.d.ts +13 -0
  93. package/dist/types-ts4.5/focus/is-in-same-layer.d.ts +8 -0
  94. package/dist/types-ts4.5/focus/is-nested-layer-focused.d.ts +8 -0
  95. package/dist/types-ts4.5/focus/selectors.d.ts +4 -0
  96. package/dist/types-ts4.5/focus/types.d.ts +5 -0
  97. package/dist/types-ts4.5/internal/role-to-aria-has-popup.d.ts +13 -0
  98. package/dist/types-ts4.5/internal/role-types.d.ts +0 -19
  99. package/dist/types-ts4.5/internal/should-focus-into-popover.d.ts +8 -0
  100. package/dist/types-ts4.5/internal/use-anchor-position-at-point.d.ts +62 -0
  101. package/dist/types-ts4.5/internal/use-anchor-position.d.ts +6 -1
  102. package/dist/types-ts4.5/placement-map/index.d.ts +1 -1
  103. package/dist/types-ts4.5/placement-map/legacy-placements.d.ts +0 -12
  104. package/dist/types-ts4.5/placement-map/placement-mapping.d.ts +13 -0
  105. package/dist/types-ts4.5/use-arrow-navigation/use-arrow-navigation.d.ts +1 -1
  106. package/package.json +1 -1
  107. package/use-anchor-position-at-point/package.json +17 -0
  108. package/dist/cjs/focus/focus.js +0 -169
  109. package/dist/cjs/focus/index.js +0 -24
  110. package/dist/es2019/focus/focus.js +0 -158
  111. package/dist/es2019/focus/index.js +0 -1
  112. package/dist/esm/focus/focus.js +0 -160
  113. package/dist/esm/focus/index.js +0 -1
  114. package/dist/types/focus/focus.d.ts +0 -50
  115. package/dist/types/focus/index.d.ts +0 -2
  116. package/dist/types-ts4.5/focus/focus.d.ts +0 -50
  117. package/dist/types-ts4.5/focus/index.d.ts +0 -2
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.placementMapping = exports.LEGACY_PLACEMENTS = void 0;
6
+ exports.LEGACY_PLACEMENTS = void 0;
7
7
  /**
8
8
  * Legacy Popper.js placement values (used by `@atlaskit/popper`,
9
9
  * `@atlaskit/popup`, `@atlaskit/tooltip`, etc.). Re-exported from
@@ -11,103 +11,4 @@ exports.placementMapping = exports.LEGACY_PLACEMENTS = void 0;
11
11
  * to enumerate every placement (e.g. for a settings dropdown). New code
12
12
  * should use the object-based `TPlacementOptions` directly.
13
13
  */
14
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
15
- var LEGACY_PLACEMENTS = exports.LEGACY_PLACEMENTS = ['auto', 'auto-start', 'auto-end', 'top', 'top-start', 'top-center', 'top-end', 'bottom', 'bottom-start', 'bottom-center', 'bottom-end', 'right', 'right-start', 'right-end', 'left', 'left-start', 'left-end'];
16
- /**
17
- * The full mapping record. Internal - use `fromLegacyPlacement` from the
18
- * public entry-point so the offset-tuple translation is applied consistently.
19
- *
20
- * Note: `auto`, `auto-start`, `auto-end` map to `block-end` placements. This
21
- * is a reduction in expressiveness vs Popper.js (which picked the best
22
- * placement at runtime). CSS `position-try-fallbacks` flips at the edge but
23
- * the initial placement is fixed. Migration consumers who relied on Popper's
24
- * dynamic auto-flip should pick an explicit placement instead.
25
- */
26
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
27
- var placementMapping = exports.placementMapping = {
28
- top: {
29
- axis: 'block',
30
- edge: 'start',
31
- align: 'center'
32
- },
33
- 'top-start': {
34
- axis: 'block',
35
- edge: 'start',
36
- align: 'start'
37
- },
38
- 'top-center': {
39
- axis: 'block',
40
- edge: 'start',
41
- align: 'center'
42
- },
43
- 'top-end': {
44
- axis: 'block',
45
- edge: 'start',
46
- align: 'end'
47
- },
48
- bottom: {
49
- axis: 'block',
50
- edge: 'end',
51
- align: 'center'
52
- },
53
- 'bottom-start': {
54
- axis: 'block',
55
- edge: 'end',
56
- align: 'start'
57
- },
58
- 'bottom-center': {
59
- axis: 'block',
60
- edge: 'end',
61
- align: 'center'
62
- },
63
- 'bottom-end': {
64
- axis: 'block',
65
- edge: 'end',
66
- align: 'end'
67
- },
68
- right: {
69
- axis: 'inline',
70
- edge: 'end',
71
- align: 'center'
72
- },
73
- 'right-start': {
74
- axis: 'inline',
75
- edge: 'end',
76
- align: 'start'
77
- },
78
- 'right-end': {
79
- axis: 'inline',
80
- edge: 'end',
81
- align: 'end'
82
- },
83
- left: {
84
- axis: 'inline',
85
- edge: 'start',
86
- align: 'center'
87
- },
88
- 'left-start': {
89
- axis: 'inline',
90
- edge: 'start',
91
- align: 'start'
92
- },
93
- 'left-end': {
94
- axis: 'inline',
95
- edge: 'start',
96
- align: 'end'
97
- },
98
- auto: {
99
- axis: 'block',
100
- edge: 'end',
101
- align: 'center'
102
- },
103
- 'auto-start': {
104
- axis: 'block',
105
- edge: 'end',
106
- align: 'start'
107
- },
108
- 'auto-end': {
109
- axis: 'block',
110
- edge: 'end',
111
- align: 'end'
112
- }
113
- };
14
+ var LEGACY_PLACEMENTS = exports.LEGACY_PLACEMENTS = ['auto', 'auto-start', 'auto-end', 'top', 'top-start', 'top-center', 'top-end', 'bottom', 'bottom-start', 'bottom-center', 'bottom-end', 'right', 'right-start', 'right-end', 'left', 'left-start', 'left-end'];
@@ -0,0 +1,103 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.placementMapping = void 0;
7
+ /**
8
+ * The full mapping record. Internal - use `fromLegacyPlacement` from the
9
+ * public entry-point so the offset-tuple translation is applied consistently.
10
+ *
11
+ * Note: `auto`, `auto-start`, `auto-end` map to `block-end` placements. This
12
+ * is a reduction in expressiveness vs Popper.js (which picked the best
13
+ * placement at runtime). CSS `position-try-fallbacks` flips at the edge but
14
+ * the initial placement is fixed. Migration consumers who relied on Popper's
15
+ * dynamic auto-flip should pick an explicit placement instead.
16
+ */
17
+ var placementMapping = exports.placementMapping = {
18
+ top: {
19
+ axis: 'block',
20
+ edge: 'start',
21
+ align: 'center'
22
+ },
23
+ 'top-start': {
24
+ axis: 'block',
25
+ edge: 'start',
26
+ align: 'start'
27
+ },
28
+ 'top-center': {
29
+ axis: 'block',
30
+ edge: 'start',
31
+ align: 'center'
32
+ },
33
+ 'top-end': {
34
+ axis: 'block',
35
+ edge: 'start',
36
+ align: 'end'
37
+ },
38
+ bottom: {
39
+ axis: 'block',
40
+ edge: 'end',
41
+ align: 'center'
42
+ },
43
+ 'bottom-start': {
44
+ axis: 'block',
45
+ edge: 'end',
46
+ align: 'start'
47
+ },
48
+ 'bottom-center': {
49
+ axis: 'block',
50
+ edge: 'end',
51
+ align: 'center'
52
+ },
53
+ 'bottom-end': {
54
+ axis: 'block',
55
+ edge: 'end',
56
+ align: 'end'
57
+ },
58
+ right: {
59
+ axis: 'inline',
60
+ edge: 'end',
61
+ align: 'center'
62
+ },
63
+ 'right-start': {
64
+ axis: 'inline',
65
+ edge: 'end',
66
+ align: 'start'
67
+ },
68
+ 'right-end': {
69
+ axis: 'inline',
70
+ edge: 'end',
71
+ align: 'end'
72
+ },
73
+ left: {
74
+ axis: 'inline',
75
+ edge: 'start',
76
+ align: 'center'
77
+ },
78
+ 'left-start': {
79
+ axis: 'inline',
80
+ edge: 'start',
81
+ align: 'start'
82
+ },
83
+ 'left-end': {
84
+ axis: 'inline',
85
+ edge: 'start',
86
+ align: 'end'
87
+ },
88
+ auto: {
89
+ axis: 'block',
90
+ edge: 'end',
91
+ align: 'center'
92
+ },
93
+ 'auto-start': {
94
+ axis: 'block',
95
+ edge: 'end',
96
+ align: 'start'
97
+ },
98
+ 'auto-end': {
99
+ axis: 'block',
100
+ edge: 'end',
101
+ align: 'end'
102
+ }
103
+ };
@@ -12,7 +12,8 @@ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")
12
12
  var _react = _interopRequireWildcard(require("react"));
13
13
  var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs"));
14
14
  var _reducedMotion = require("../internal/reduced-motion");
15
- var _roleTypes = require("../internal/role-types");
15
+ var _roleToAriaHasPopup = require("../internal/role-to-aria-has-popup");
16
+ var _shouldFocusIntoPopover = require("../internal/should-focus-into-popover");
16
17
  var _useAnchorPosition = require("../internal/use-anchor-position");
17
18
  var _useWidthFromAnchor = require("../internal/use-width-from-anchor");
18
19
  var _popover = require("../popover/popover");
@@ -100,7 +101,7 @@ var PopupContent = exports.PopupContent = /*#__PURE__*/(0, _react.forwardRef)(fu
100
101
  // Sync aria-haspopup on the trigger with the content's role
101
102
  var setAriaHasPopup = ctx === null || ctx === void 0 ? void 0 : ctx.setAriaHasPopup;
102
103
  (0, _react.useLayoutEffect)(function () {
103
- setAriaHasPopup === null || setAriaHasPopup === void 0 || setAriaHasPopup((0, _roleTypes.roleToAriaHasPopup)({
104
+ setAriaHasPopup === null || setAriaHasPopup === void 0 || setAriaHasPopup((0, _roleToAriaHasPopup.roleToAriaHasPopup)({
104
105
  role: role
105
106
  }));
106
107
  }, [role, setAriaHasPopup]);
@@ -151,7 +152,7 @@ var PopupContent = exports.PopupContent = /*#__PURE__*/(0, _react.forwardRef)(fu
151
152
  // Single source of truth for "this role moves focus on open
152
153
  // and therefore needs to be restored on close" - see
153
154
  // `shouldFocusIntoPopover` in `internal/role-types.tsx`.
154
- if (focusStillInPopover && (0, _roleTypes.shouldFocusIntoPopover)({
155
+ if (focusStillInPopover && (0, _shouldFocusIntoPopover.shouldFocusIntoPopover)({
155
156
  role: role
156
157
  })) {
157
158
  trigger.focus({
@@ -8,7 +8,9 @@ var _react = require("react");
8
8
  var _bindEventListener = require("bind-event-listener");
9
9
  var _browserApis = require("@atlaskit/browser-apis");
10
10
  var _keycodes = require("@atlaskit/ds-lib/keycodes");
11
- var _focus = require("../focus");
11
+ var _getFirstFocusable2 = require("../focus/get-first-focusable");
12
+ var _getLastFocusable2 = require("../focus/get-last-focusable");
13
+ var _getNextFocusable3 = require("../focus/get-next-focusable");
12
14
  var menuScope = '[role="menu"]';
13
15
 
14
16
  /**
@@ -96,7 +98,7 @@ function useArrowNavigation(_ref) {
96
98
  var _getNextFocusable;
97
99
  // Prevent page scroll while navigating menu items.
98
100
  event.preventDefault();
99
- (_getNextFocusable = (0, _focus.getNextFocusable)({
101
+ (_getNextFocusable = (0, _getNextFocusable3.getNextFocusable)({
100
102
  container: container,
101
103
  direction: 'forwards',
102
104
  filter: filter
@@ -107,7 +109,7 @@ function useArrowNavigation(_ref) {
107
109
  var _getNextFocusable2;
108
110
  // Prevent page scroll while navigating menu items.
109
111
  event.preventDefault();
110
- (_getNextFocusable2 = (0, _focus.getNextFocusable)({
112
+ (_getNextFocusable2 = (0, _getNextFocusable3.getNextFocusable)({
111
113
  container: container,
112
114
  direction: 'backwards',
113
115
  filter: filter
@@ -118,7 +120,7 @@ function useArrowNavigation(_ref) {
118
120
  var _getFirstFocusable;
119
121
  // Prevent browser default (e.g. scrolling to top of page).
120
122
  event.preventDefault();
121
- (_getFirstFocusable = (0, _focus.getFirstFocusable)({
123
+ (_getFirstFocusable = (0, _getFirstFocusable2.getFirstFocusable)({
122
124
  container: container,
123
125
  filter: filter
124
126
  })) === null || _getFirstFocusable === void 0 || _getFirstFocusable.focus();
@@ -128,7 +130,7 @@ function useArrowNavigation(_ref) {
128
130
  var _getLastFocusable;
129
131
  // Prevent browser default (e.g. scrolling to bottom of page).
130
132
  event.preventDefault();
131
- (_getLastFocusable = (0, _focus.getLastFocusable)({
133
+ (_getLastFocusable = (0, _getLastFocusable2.getLastFocusable)({
132
134
  container: container,
133
135
  filter: filter
134
136
  })) === null || _getLastFocusable === void 0 || _getLastFocusable.focus();
@@ -1 +1,3 @@
1
- export { getFirstFocusable, getLastFocusable, getNextFocusable } from '../focus';
1
+ export { getFirstFocusable } from '../focus/get-first-focusable';
2
+ export { getLastFocusable } from '../focus/get-last-focusable';
3
+ export { getNextFocusable } from '../focus/get-next-focusable';
@@ -0,0 +1 @@
1
+ export { useAnchorPositionAtPoint } from '../internal/use-anchor-position-at-point';
@@ -0,0 +1,14 @@
1
+ import { getFocusables } from './get-focusables';
2
+ /**
3
+ * Returns the first focusable element within the container.
4
+ */
5
+ export function getFirstFocusable({
6
+ container,
7
+ filter
8
+ }) {
9
+ const items = getFocusables({
10
+ container,
11
+ filter
12
+ });
13
+ return items.length > 0 ? items[0] : null;
14
+ }
@@ -0,0 +1,19 @@
1
+ import { isInSameLayer } from './is-in-same-layer';
2
+ import { selectors } from './selectors';
3
+ /**
4
+ * Returns all focusable HTMLElements within the container, optionally filtered.
5
+ *
6
+ * Focusables that belong to a nested top-layer scope (a `[popover]` or
7
+ * `<dialog>` descendant of the container) are excluded - those elements
8
+ * are owned by the inner layer's focus management. The container itself
9
+ * is allowed to be a popover/dialog; only nested ones are filtered.
10
+ */
11
+ export function getFocusables({
12
+ container,
13
+ filter
14
+ }) {
15
+ return Array.from(container.querySelectorAll(selectors.focusable)).filter(element => element instanceof HTMLElement).filter(element => isInSameLayer({
16
+ element,
17
+ container
18
+ })).filter(element => !filter || filter(element, container));
19
+ }
@@ -0,0 +1,14 @@
1
+ import { getFocusables } from './get-focusables';
2
+ /**
3
+ * Returns the last focusable element within the container.
4
+ */
5
+ export function getLastFocusable({
6
+ container,
7
+ filter
8
+ }) {
9
+ const items = getFocusables({
10
+ container,
11
+ filter
12
+ });
13
+ return items.length > 0 ? items[items.length - 1] : null;
14
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Returns the next focusable element in the given direction relative to the
3
+ * currently focused element within the container. Wraps around at both ends.
4
+ *
5
+ * Returns `null` if there is no focused element within the container or if
6
+ * the focused element is not in the list of focusable elements.
7
+ */
8
+
9
+ import { getFocusables } from './get-focusables';
10
+ import { selectors } from './selectors';
11
+ export function getNextFocusable({
12
+ container,
13
+ direction,
14
+ filter
15
+ }) {
16
+ var _all$nextIndex;
17
+ const all = getFocusables({
18
+ container,
19
+ filter
20
+ });
21
+ const current = container.querySelector(selectors.focused);
22
+ if (!(current instanceof HTMLElement)) {
23
+ return null;
24
+ }
25
+ const index = all.indexOf(current);
26
+ if (index === -1) {
27
+ return null;
28
+ }
29
+ const nextIndex = (() => {
30
+ if (direction === 'forwards') {
31
+ return (index + 1) % all.length;
32
+ }
33
+ const proposed = index - 1;
34
+ // if going into negative numbers, loop back to the last item
35
+ return proposed < 0 ? all.length - 1 : proposed;
36
+ })();
37
+ return (_all$nextIndex = all[nextIndex]) !== null && _all$nextIndex !== void 0 ? _all$nextIndex : null;
38
+ }
@@ -0,0 +1,18 @@
1
+ const topLayerSelector = '[popover], dialog, [role="dialog"], [role="alertdialog"]';
2
+
3
+ /**
4
+ * Returns true when `element` and `container` resolve to the same nearest
5
+ * top-layer host (or both resolve to `null`, meaning the document layer).
6
+ */
7
+ export function isInSameLayer({
8
+ element,
9
+ container
10
+ }) {
11
+ const elementLayer = element.closest(topLayerSelector);
12
+ const containerLayer = container.closest(topLayerSelector);
13
+ // Both elements are in the "document" layer
14
+ if (!elementLayer && !containerLayer) {
15
+ return true;
16
+ }
17
+ return elementLayer === containerLayer;
18
+ }
@@ -0,0 +1,28 @@
1
+ import { getDocument } from '@atlaskit/browser-apis';
2
+ import { isInSameLayer } from './is-in-same-layer';
3
+
4
+ /**
5
+ * Returns `true` when `document.activeElement` is inside a nested top-layer
6
+ * descendant of `container` (a `[popover]`, `<dialog>`, `[role="dialog"]` or
7
+ * `[role="alertdialog"]` that is itself a descendant of `container`).
8
+ */
9
+ export function isNestedLayerFocused({
10
+ container
11
+ }) {
12
+ var _getDocument;
13
+ const focused = (_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.activeElement;
14
+ if (!(focused instanceof HTMLElement)) {
15
+ return false;
16
+ }
17
+
18
+ // Cannot be in a nested layer if it is not in the container
19
+ if (!container.contains(focused)) {
20
+ return false;
21
+ }
22
+
23
+ // If not in the same layer, must be in a nested layer
24
+ return !isInSameLayer({
25
+ element: focused,
26
+ container
27
+ });
28
+ }
@@ -0,0 +1,11 @@
1
+ export const selectors = (() => {
2
+ // Common exclusion filter applied to every selector.
3
+ const not = ':not([tabindex="-1"]):not([aria-disabled="true"]):not([aria-hidden="true"])';
4
+ const notDisabled = `:not([disabled])${not}`;
5
+ const focusable = [`a[href]${not}`, `area[href]${not}`, `input${notDisabled}:not([type="hidden"])`, `select${notDisabled}`, `textarea${notDisabled}`, `button${notDisabled}`, `iframe${not}`, `audio[controls]${not}`, `video[controls]${not}`, `[contenteditable]:not([contenteditable="false"])${not}`, `[tabindex]${notDisabled}${not}`];
6
+ return {
7
+ focusable: focusable.join(','),
8
+ // Build the `:focus` selector
9
+ focused: focusable.map(selector => `${selector}:focus`).join(',')
10
+ };
11
+ })();
File without changes
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Maps a popover content role to the correct `aria-haspopup` value
3
+ * for its trigger element.
4
+ *
5
+ * Returns `undefined` for roles that are not popups in the ARIA sense
6
+ * (`tooltip`, `note`, `status`, `alert`, `log`). Triggers for those roles
7
+ * should not announce a popup at all - emitting `aria-haspopup="dialog"`
8
+ * on a tooltip trigger would mislead assistive technology.
9
+ */
10
+ export function roleToAriaHasPopup({
11
+ role
12
+ }) {
13
+ if (role === 'menu') {
14
+ return 'menu';
15
+ }
16
+ if (role === 'listbox') {
17
+ return 'listbox';
18
+ }
19
+ if (role === 'tree') {
20
+ return 'tree';
21
+ }
22
+ if (role === 'grid') {
23
+ return 'grid';
24
+ }
25
+ if (role === 'dialog' || role === 'alertdialog') {
26
+ return 'dialog';
27
+ }
28
+ // Non-popup roles (tooltip, note, status, alert, log) and the
29
+ // no-role case: omit `aria-haspopup` entirely. React drops the
30
+ // attribute when the value is `undefined`.
31
+ return undefined;
32
+ }
@@ -1,92 +0,0 @@
1
- /**
2
- * Valid values for the `aria-haspopup` attribute.
3
- *
4
- * Derived from the HTML spec - maps popover roles to what the trigger
5
- * announces. `undefined` means the attribute is omitted entirely (used for
6
- * non-popup roles like `tooltip`/`status`/`alert`/`note`/`log` where
7
- * `aria-haspopup` would be misleading).
8
- *
9
- * `true` is intentionally NOT in the union - the runtime only ever produces
10
- * the explicit string forms, so widening the type would invite consumers to
11
- * pass `true` and get an `aria-haspopup="true"` serialisation that the
12
- * runtime no longer emits.
13
- */
14
-
15
- /**
16
- * Roles that require an accessible name (`label` or `labelledBy`) per WCAG 4.1.2.
17
- * Without a name, assistive technology cannot identify these landmarks.
18
- */
19
-
20
- /**
21
- * Roles where the accessible name is derived from content or provided externally
22
- * (e.g. a combobox labels its listbox, tooltip content IS the accessible name).
23
- */
24
-
25
- // Shared ARIA role discrimination types
26
- //
27
- // These encode the WCAG 4.1.2 constraint: roles like `dialog`, `alertdialog`,
28
- // and `menu` MUST have an accessible name (either `label` or `labelledBy`).
29
- // TypeScript enforces this at compile time via discriminated unions.
30
-
31
- /**
32
- * ARIA role props where a role is always required.
33
- *
34
- * Used by `TPopupContentProps`. Popup content must always declare
35
- * its role so assistive technology can identify the layer.
36
- */
37
-
38
- /**
39
- * ARIA role props where a role is optional.
40
- *
41
- * Used by `TPopoverProps`. The low-level primitive allows omitting
42
- * the role for cases where it is set externally or not needed.
43
- */
44
-
45
- /**
46
- * Maps a popover content role to the correct `aria-haspopup` value
47
- * for its trigger element.
48
- *
49
- * Returns `undefined` for roles that are not popups in the ARIA sense
50
- * (`tooltip`, `note`, `status`, `alert`, `log`). Triggers for those roles
51
- * should not announce a popup at all - emitting `aria-haspopup="dialog"`
52
- * on a tooltip trigger would mislead assistive technology.
53
- */
54
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
55
- export function roleToAriaHasPopup({
56
- role
57
- }) {
58
- if (role === 'menu') {
59
- return 'menu';
60
- }
61
- if (role === 'listbox') {
62
- return 'listbox';
63
- }
64
- if (role === 'tree') {
65
- return 'tree';
66
- }
67
- if (role === 'grid') {
68
- return 'grid';
69
- }
70
- if (role === 'dialog' || role === 'alertdialog') {
71
- return 'dialog';
72
- }
73
- // Non-popup roles (tooltip, note, status, alert, log) and the
74
- // no-role case: omit `aria-haspopup` entirely. React drops the
75
- // attribute when the value is `undefined`.
76
- return undefined;
77
- }
78
- const rolesThatMoveFocus = new Set(['dialog', 'alertdialog', 'menu', 'listbox', 'tree', 'grid']);
79
-
80
- /**
81
- * Returns `true` when the given popover role moves focus into the popover
82
- * on open, and `false` otherwise (including when `role` is `undefined`).
83
- */
84
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
85
- export function shouldFocusIntoPopover({
86
- role
87
- }) {
88
- if (!role) {
89
- return false;
90
- }
91
- return rolesThatMoveFocus.has(role);
92
- }
@@ -0,0 +1,14 @@
1
+ const rolesThatMoveFocus = new Set(['dialog', 'alertdialog', 'menu', 'listbox', 'tree', 'grid']);
2
+
3
+ /**
4
+ * Returns `true` when the given popover role moves focus into the popover
5
+ * on open, and `false` otherwise (including when `role` is `undefined`).
6
+ */
7
+ export function shouldFocusIntoPopover({
8
+ role
9
+ }) {
10
+ if (!role) {
11
+ return false;
12
+ }
13
+ return rolesThatMoveFocus.has(role);
14
+ }