@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.
- package/CHANGELOG.md +16 -0
- package/dist/cjs/entry-points/focus.js +6 -4
- package/dist/cjs/entry-points/use-anchor-position-at-point.js +12 -0
- package/dist/cjs/focus/get-first-focusable.js +19 -0
- package/dist/cjs/focus/get-focusables.js +30 -0
- package/dist/cjs/focus/get-last-focusable.js +19 -0
- package/dist/cjs/focus/get-next-focusable.js +43 -0
- package/dist/cjs/focus/is-in-same-layer.js +23 -0
- package/dist/cjs/focus/is-nested-layer-focused.js +32 -0
- package/dist/cjs/focus/selectors.js +19 -0
- package/dist/cjs/focus/types.js +1 -0
- package/dist/cjs/internal/role-to-aria-has-popup.js +37 -0
- package/dist/cjs/internal/role-types.js +1 -97
- package/dist/cjs/internal/should-focus-into-popover.js +19 -0
- package/dist/cjs/internal/use-anchor-position-at-point.js +127 -0
- package/dist/cjs/internal/use-anchor-position.js +7 -3
- package/dist/cjs/internal/use-focus-wrap.js +8 -5
- package/dist/cjs/internal/use-initial-focus.js +4 -4
- package/dist/cjs/placement-map/index.js +2 -2
- package/dist/cjs/placement-map/legacy-placements.js +2 -101
- package/dist/cjs/placement-map/placement-mapping.js +103 -0
- package/dist/cjs/popup/popup-content.js +4 -3
- package/dist/cjs/use-arrow-navigation/use-arrow-navigation.js +7 -5
- package/dist/es2019/entry-points/focus.js +3 -1
- package/dist/es2019/entry-points/use-anchor-position-at-point.js +1 -0
- package/dist/es2019/focus/get-first-focusable.js +14 -0
- package/dist/es2019/focus/get-focusables.js +19 -0
- package/dist/es2019/focus/get-last-focusable.js +14 -0
- package/dist/es2019/focus/get-next-focusable.js +38 -0
- package/dist/es2019/focus/is-in-same-layer.js +18 -0
- package/dist/es2019/focus/is-nested-layer-focused.js +28 -0
- package/dist/es2019/focus/selectors.js +11 -0
- package/dist/es2019/focus/types.js +0 -0
- package/dist/es2019/internal/role-to-aria-has-popup.js +32 -0
- package/dist/es2019/internal/role-types.js +0 -92
- package/dist/es2019/internal/should-focus-into-popover.js +14 -0
- package/dist/es2019/internal/use-anchor-position-at-point.js +122 -0
- package/dist/es2019/internal/use-anchor-position.js +6 -3
- package/dist/es2019/internal/use-focus-wrap.js +4 -1
- package/dist/es2019/internal/use-initial-focus.js +1 -1
- package/dist/es2019/placement-map/index.js +1 -1
- package/dist/es2019/placement-map/legacy-placements.js +1 -100
- package/dist/es2019/placement-map/placement-mapping.js +97 -0
- package/dist/es2019/popup/popup-content.js +2 -1
- package/dist/es2019/use-arrow-navigation/use-arrow-navigation.js +3 -1
- package/dist/esm/entry-points/focus.js +3 -1
- package/dist/esm/entry-points/use-anchor-position-at-point.js +1 -0
- package/dist/esm/focus/get-first-focusable.js +13 -0
- package/dist/esm/focus/get-focusables.js +24 -0
- package/dist/esm/focus/get-last-focusable.js +13 -0
- package/dist/esm/focus/get-next-focusable.js +37 -0
- package/dist/esm/focus/is-in-same-layer.js +17 -0
- package/dist/esm/focus/is-nested-layer-focused.js +27 -0
- package/dist/esm/focus/selectors.js +13 -0
- package/dist/esm/focus/types.js +0 -0
- package/dist/esm/internal/role-to-aria-has-popup.js +31 -0
- package/dist/esm/internal/role-types.js +0 -90
- package/dist/esm/internal/should-focus-into-popover.js +13 -0
- package/dist/esm/internal/use-anchor-position-at-point.js +122 -0
- package/dist/esm/internal/use-anchor-position.js +7 -3
- package/dist/esm/internal/use-focus-wrap.js +4 -1
- package/dist/esm/internal/use-initial-focus.js +1 -1
- package/dist/esm/placement-map/index.js +1 -1
- package/dist/esm/placement-map/legacy-placements.js +1 -100
- package/dist/esm/placement-map/placement-mapping.js +97 -0
- package/dist/esm/popup/popup-content.js +2 -1
- package/dist/esm/use-arrow-navigation/use-arrow-navigation.js +3 -1
- package/dist/types/entry-points/focus.d.ts +4 -2
- package/dist/types/entry-points/use-anchor-position-at-point.d.ts +1 -0
- package/dist/types/focus/get-first-focusable.d.ts +8 -0
- package/dist/types/focus/get-focusables.d.ts +13 -0
- package/dist/types/focus/get-last-focusable.d.ts +8 -0
- package/dist/types/focus/get-next-focusable.d.ts +13 -0
- package/dist/types/focus/is-in-same-layer.d.ts +8 -0
- package/dist/types/focus/is-nested-layer-focused.d.ts +8 -0
- package/dist/types/focus/selectors.d.ts +4 -0
- package/dist/types/focus/types.d.ts +5 -0
- package/dist/types/internal/role-to-aria-has-popup.d.ts +13 -0
- package/dist/types/internal/role-types.d.ts +0 -19
- package/dist/types/internal/should-focus-into-popover.d.ts +8 -0
- package/dist/types/internal/use-anchor-position-at-point.d.ts +62 -0
- package/dist/types/internal/use-anchor-position.d.ts +6 -1
- package/dist/types/placement-map/index.d.ts +1 -1
- package/dist/types/placement-map/legacy-placements.d.ts +0 -12
- package/dist/types/placement-map/placement-mapping.d.ts +13 -0
- package/dist/types/use-arrow-navigation/use-arrow-navigation.d.ts +1 -1
- package/dist/types-ts4.5/entry-points/focus.d.ts +4 -2
- package/dist/types-ts4.5/entry-points/use-anchor-position-at-point.d.ts +1 -0
- package/dist/types-ts4.5/focus/get-first-focusable.d.ts +8 -0
- package/dist/types-ts4.5/focus/get-focusables.d.ts +13 -0
- package/dist/types-ts4.5/focus/get-last-focusable.d.ts +8 -0
- package/dist/types-ts4.5/focus/get-next-focusable.d.ts +13 -0
- package/dist/types-ts4.5/focus/is-in-same-layer.d.ts +8 -0
- package/dist/types-ts4.5/focus/is-nested-layer-focused.d.ts +8 -0
- package/dist/types-ts4.5/focus/selectors.d.ts +4 -0
- package/dist/types-ts4.5/focus/types.d.ts +5 -0
- package/dist/types-ts4.5/internal/role-to-aria-has-popup.d.ts +13 -0
- package/dist/types-ts4.5/internal/role-types.d.ts +0 -19
- package/dist/types-ts4.5/internal/should-focus-into-popover.d.ts +8 -0
- package/dist/types-ts4.5/internal/use-anchor-position-at-point.d.ts +62 -0
- package/dist/types-ts4.5/internal/use-anchor-position.d.ts +6 -1
- package/dist/types-ts4.5/placement-map/index.d.ts +1 -1
- package/dist/types-ts4.5/placement-map/legacy-placements.d.ts +0 -12
- package/dist/types-ts4.5/placement-map/placement-mapping.d.ts +13 -0
- package/dist/types-ts4.5/use-arrow-navigation/use-arrow-navigation.d.ts +1 -1
- package/package.json +1 -1
- package/use-anchor-position-at-point/package.json +17 -0
- package/dist/cjs/focus/focus.js +0 -169
- package/dist/cjs/focus/index.js +0 -24
- package/dist/es2019/focus/focus.js +0 -158
- package/dist/es2019/focus/index.js +0 -1
- package/dist/esm/focus/focus.js +0 -160
- package/dist/esm/focus/index.js +0 -1
- package/dist/types/focus/focus.d.ts +0 -50
- package/dist/types/focus/index.d.ts +0 -2
- package/dist/types-ts4.5/focus/focus.d.ts +0 -50
- package/dist/types-ts4.5/focus/index.d.ts +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @atlaskit/top-layer
|
|
2
2
|
|
|
3
|
+
## 0.13.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`cf4af9aa9a2fe`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/cf4af9aa9a2fe) -
|
|
8
|
+
Fix tooltip with `position="mouse-x"` / `position="mouse-y"` not respecting the `mousePosition`
|
|
9
|
+
prop when the `platform-dst-top-layer` feature flag is enabled.
|
|
10
|
+
|
|
11
|
+
The cursor-anchored tooltip is now positioned via a new `useAnchorPositionAtPoint` hook in
|
|
12
|
+
`@atlaskit/top-layer`, which delegates to `useAnchorPosition` against a hidden anchor element
|
|
13
|
+
placed once at the cursor location at show time. The popover stays anchored at that location and
|
|
14
|
+
does not follow subsequent pointer movement.
|
|
15
|
+
|
|
16
|
+
`useAnchorPosition` now also accepts an optional `isEnabled` prop (defaults to `true`); when
|
|
17
|
+
`false`, the hook is a no-op.
|
|
18
|
+
|
|
3
19
|
## 0.12.2
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -6,19 +6,21 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
Object.defineProperty(exports, "getFirstFocusable", {
|
|
7
7
|
enumerable: true,
|
|
8
8
|
get: function get() {
|
|
9
|
-
return
|
|
9
|
+
return _getFirstFocusable.getFirstFocusable;
|
|
10
10
|
}
|
|
11
11
|
});
|
|
12
12
|
Object.defineProperty(exports, "getLastFocusable", {
|
|
13
13
|
enumerable: true,
|
|
14
14
|
get: function get() {
|
|
15
|
-
return
|
|
15
|
+
return _getLastFocusable.getLastFocusable;
|
|
16
16
|
}
|
|
17
17
|
});
|
|
18
18
|
Object.defineProperty(exports, "getNextFocusable", {
|
|
19
19
|
enumerable: true,
|
|
20
20
|
get: function get() {
|
|
21
|
-
return
|
|
21
|
+
return _getNextFocusable.getNextFocusable;
|
|
22
22
|
}
|
|
23
23
|
});
|
|
24
|
-
var
|
|
24
|
+
var _getFirstFocusable = require("../focus/get-first-focusable");
|
|
25
|
+
var _getLastFocusable = require("../focus/get-last-focusable");
|
|
26
|
+
var _getNextFocusable = require("../focus/get-next-focusable");
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "useAnchorPositionAtPoint", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function get() {
|
|
9
|
+
return _useAnchorPositionAtPoint.useAnchorPositionAtPoint;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
var _useAnchorPositionAtPoint = require("../internal/use-anchor-position-at-point");
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getFirstFocusable = getFirstFocusable;
|
|
7
|
+
var _getFocusables = require("./get-focusables");
|
|
8
|
+
/**
|
|
9
|
+
* Returns the first focusable element within the container.
|
|
10
|
+
*/
|
|
11
|
+
function getFirstFocusable(_ref) {
|
|
12
|
+
var container = _ref.container,
|
|
13
|
+
filter = _ref.filter;
|
|
14
|
+
var items = (0, _getFocusables.getFocusables)({
|
|
15
|
+
container: container,
|
|
16
|
+
filter: filter
|
|
17
|
+
});
|
|
18
|
+
return items.length > 0 ? items[0] : null;
|
|
19
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getFocusables = getFocusables;
|
|
7
|
+
var _isInSameLayer = require("./is-in-same-layer");
|
|
8
|
+
var _selectors = require("./selectors");
|
|
9
|
+
/**
|
|
10
|
+
* Returns all focusable HTMLElements within the container, optionally filtered.
|
|
11
|
+
*
|
|
12
|
+
* Focusables that belong to a nested top-layer scope (a `[popover]` or
|
|
13
|
+
* `<dialog>` descendant of the container) are excluded - those elements
|
|
14
|
+
* are owned by the inner layer's focus management. The container itself
|
|
15
|
+
* is allowed to be a popover/dialog; only nested ones are filtered.
|
|
16
|
+
*/
|
|
17
|
+
function getFocusables(_ref) {
|
|
18
|
+
var container = _ref.container,
|
|
19
|
+
filter = _ref.filter;
|
|
20
|
+
return Array.from(container.querySelectorAll(_selectors.selectors.focusable)).filter(function (element) {
|
|
21
|
+
return element instanceof HTMLElement;
|
|
22
|
+
}).filter(function (element) {
|
|
23
|
+
return (0, _isInSameLayer.isInSameLayer)({
|
|
24
|
+
element: element,
|
|
25
|
+
container: container
|
|
26
|
+
});
|
|
27
|
+
}).filter(function (element) {
|
|
28
|
+
return !filter || filter(element, container);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getLastFocusable = getLastFocusable;
|
|
7
|
+
var _getFocusables = require("./get-focusables");
|
|
8
|
+
/**
|
|
9
|
+
* Returns the last focusable element within the container.
|
|
10
|
+
*/
|
|
11
|
+
function getLastFocusable(_ref) {
|
|
12
|
+
var container = _ref.container,
|
|
13
|
+
filter = _ref.filter;
|
|
14
|
+
var items = (0, _getFocusables.getFocusables)({
|
|
15
|
+
container: container,
|
|
16
|
+
filter: filter
|
|
17
|
+
});
|
|
18
|
+
return items.length > 0 ? items[items.length - 1] : null;
|
|
19
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getNextFocusable = getNextFocusable;
|
|
7
|
+
var _getFocusables = require("./get-focusables");
|
|
8
|
+
var _selectors = require("./selectors");
|
|
9
|
+
/**
|
|
10
|
+
* Returns the next focusable element in the given direction relative to the
|
|
11
|
+
* currently focused element within the container. Wraps around at both ends.
|
|
12
|
+
*
|
|
13
|
+
* Returns `null` if there is no focused element within the container or if
|
|
14
|
+
* the focused element is not in the list of focusable elements.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
function getNextFocusable(_ref) {
|
|
18
|
+
var _all$nextIndex;
|
|
19
|
+
var container = _ref.container,
|
|
20
|
+
direction = _ref.direction,
|
|
21
|
+
filter = _ref.filter;
|
|
22
|
+
var all = (0, _getFocusables.getFocusables)({
|
|
23
|
+
container: container,
|
|
24
|
+
filter: filter
|
|
25
|
+
});
|
|
26
|
+
var current = container.querySelector(_selectors.selectors.focused);
|
|
27
|
+
if (!(current instanceof HTMLElement)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
var index = all.indexOf(current);
|
|
31
|
+
if (index === -1) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
var nextIndex = function () {
|
|
35
|
+
if (direction === 'forwards') {
|
|
36
|
+
return (index + 1) % all.length;
|
|
37
|
+
}
|
|
38
|
+
var proposed = index - 1;
|
|
39
|
+
// if going into negative numbers, loop back to the last item
|
|
40
|
+
return proposed < 0 ? all.length - 1 : proposed;
|
|
41
|
+
}();
|
|
42
|
+
return (_all$nextIndex = all[nextIndex]) !== null && _all$nextIndex !== void 0 ? _all$nextIndex : null;
|
|
43
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isInSameLayer = isInSameLayer;
|
|
7
|
+
var topLayerSelector = '[popover], dialog, [role="dialog"], [role="alertdialog"]';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Returns true when `element` and `container` resolve to the same nearest
|
|
11
|
+
* top-layer host (or both resolve to `null`, meaning the document layer).
|
|
12
|
+
*/
|
|
13
|
+
function isInSameLayer(_ref) {
|
|
14
|
+
var element = _ref.element,
|
|
15
|
+
container = _ref.container;
|
|
16
|
+
var elementLayer = element.closest(topLayerSelector);
|
|
17
|
+
var containerLayer = container.closest(topLayerSelector);
|
|
18
|
+
// Both elements are in the "document" layer
|
|
19
|
+
if (!elementLayer && !containerLayer) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
return elementLayer === containerLayer;
|
|
23
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isNestedLayerFocused = isNestedLayerFocused;
|
|
7
|
+
var _browserApis = require("@atlaskit/browser-apis");
|
|
8
|
+
var _isInSameLayer = require("./is-in-same-layer");
|
|
9
|
+
/**
|
|
10
|
+
* Returns `true` when `document.activeElement` is inside a nested top-layer
|
|
11
|
+
* descendant of `container` (a `[popover]`, `<dialog>`, `[role="dialog"]` or
|
|
12
|
+
* `[role="alertdialog"]` that is itself a descendant of `container`).
|
|
13
|
+
*/
|
|
14
|
+
function isNestedLayerFocused(_ref) {
|
|
15
|
+
var _getDocument;
|
|
16
|
+
var container = _ref.container;
|
|
17
|
+
var focused = (_getDocument = (0, _browserApis.getDocument)()) === null || _getDocument === void 0 ? void 0 : _getDocument.activeElement;
|
|
18
|
+
if (!(focused instanceof HTMLElement)) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Cannot be in a nested layer if it is not in the container
|
|
23
|
+
if (!container.contains(focused)) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// If not in the same layer, must be in a nested layer
|
|
28
|
+
return !(0, _isInSameLayer.isInSameLayer)({
|
|
29
|
+
element: focused,
|
|
30
|
+
container: container
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.selectors = void 0;
|
|
7
|
+
var selectors = exports.selectors = function () {
|
|
8
|
+
// Common exclusion filter applied to every selector.
|
|
9
|
+
var not = ':not([tabindex="-1"]):not([aria-disabled="true"]):not([aria-hidden="true"])';
|
|
10
|
+
var notDisabled = ":not([disabled])".concat(not);
|
|
11
|
+
var focusable = ["a[href]".concat(not), "area[href]".concat(not), "input".concat(notDisabled, ":not([type=\"hidden\"])"), "select".concat(notDisabled), "textarea".concat(notDisabled), "button".concat(notDisabled), "iframe".concat(not), "audio[controls]".concat(not), "video[controls]".concat(not), "[contenteditable]:not([contenteditable=\"false\"])".concat(not), "[tabindex]".concat(notDisabled).concat(not)];
|
|
12
|
+
return {
|
|
13
|
+
focusable: focusable.join(','),
|
|
14
|
+
// Build the `:focus` selector
|
|
15
|
+
focused: focusable.map(function (selector) {
|
|
16
|
+
return "".concat(selector, ":focus");
|
|
17
|
+
}).join(',')
|
|
18
|
+
};
|
|
19
|
+
}();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.roleToAriaHasPopup = roleToAriaHasPopup;
|
|
7
|
+
/**
|
|
8
|
+
* Maps a popover content role to the correct `aria-haspopup` value
|
|
9
|
+
* for its trigger element.
|
|
10
|
+
*
|
|
11
|
+
* Returns `undefined` for roles that are not popups in the ARIA sense
|
|
12
|
+
* (`tooltip`, `note`, `status`, `alert`, `log`). Triggers for those roles
|
|
13
|
+
* should not announce a popup at all - emitting `aria-haspopup="dialog"`
|
|
14
|
+
* on a tooltip trigger would mislead assistive technology.
|
|
15
|
+
*/
|
|
16
|
+
function roleToAriaHasPopup(_ref) {
|
|
17
|
+
var role = _ref.role;
|
|
18
|
+
if (role === 'menu') {
|
|
19
|
+
return 'menu';
|
|
20
|
+
}
|
|
21
|
+
if (role === 'listbox') {
|
|
22
|
+
return 'listbox';
|
|
23
|
+
}
|
|
24
|
+
if (role === 'tree') {
|
|
25
|
+
return 'tree';
|
|
26
|
+
}
|
|
27
|
+
if (role === 'grid') {
|
|
28
|
+
return 'grid';
|
|
29
|
+
}
|
|
30
|
+
if (role === 'dialog' || role === 'alertdialog') {
|
|
31
|
+
return 'dialog';
|
|
32
|
+
}
|
|
33
|
+
// Non-popup roles (tooltip, note, status, alert, log) and the
|
|
34
|
+
// no-role case: omit `aria-haspopup` entirely. React drops the
|
|
35
|
+
// attribute when the value is `undefined`.
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
@@ -1,97 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.roleToAriaHasPopup = roleToAriaHasPopup;
|
|
7
|
-
exports.shouldFocusIntoPopover = shouldFocusIntoPopover;
|
|
8
|
-
/**
|
|
9
|
-
* Valid values for the `aria-haspopup` attribute.
|
|
10
|
-
*
|
|
11
|
-
* Derived from the HTML spec - maps popover roles to what the trigger
|
|
12
|
-
* announces. `undefined` means the attribute is omitted entirely (used for
|
|
13
|
-
* non-popup roles like `tooltip`/`status`/`alert`/`note`/`log` where
|
|
14
|
-
* `aria-haspopup` would be misleading).
|
|
15
|
-
*
|
|
16
|
-
* `true` is intentionally NOT in the union - the runtime only ever produces
|
|
17
|
-
* the explicit string forms, so widening the type would invite consumers to
|
|
18
|
-
* pass `true` and get an `aria-haspopup="true"` serialisation that the
|
|
19
|
-
* runtime no longer emits.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Roles that require an accessible name (`label` or `labelledBy`) per WCAG 4.1.2.
|
|
24
|
-
* Without a name, assistive technology cannot identify these landmarks.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Roles where the accessible name is derived from content or provided externally
|
|
29
|
-
* (e.g. a combobox labels its listbox, tooltip content IS the accessible name).
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
// Shared ARIA role discrimination types
|
|
33
|
-
//
|
|
34
|
-
// These encode the WCAG 4.1.2 constraint: roles like `dialog`, `alertdialog`,
|
|
35
|
-
// and `menu` MUST have an accessible name (either `label` or `labelledBy`).
|
|
36
|
-
// TypeScript enforces this at compile time via discriminated unions.
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* ARIA role props where a role is always required.
|
|
40
|
-
*
|
|
41
|
-
* Used by `TPopupContentProps`. Popup content must always declare
|
|
42
|
-
* its role so assistive technology can identify the layer.
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* ARIA role props where a role is optional.
|
|
47
|
-
*
|
|
48
|
-
* Used by `TPopoverProps`. The low-level primitive allows omitting
|
|
49
|
-
* the role for cases where it is set externally or not needed.
|
|
50
|
-
*/
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Maps a popover content role to the correct `aria-haspopup` value
|
|
54
|
-
* for its trigger element.
|
|
55
|
-
*
|
|
56
|
-
* Returns `undefined` for roles that are not popups in the ARIA sense
|
|
57
|
-
* (`tooltip`, `note`, `status`, `alert`, `log`). Triggers for those roles
|
|
58
|
-
* should not announce a popup at all - emitting `aria-haspopup="dialog"`
|
|
59
|
-
* on a tooltip trigger would mislead assistive technology.
|
|
60
|
-
*/
|
|
61
|
-
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
|
|
62
|
-
function roleToAriaHasPopup(_ref) {
|
|
63
|
-
var role = _ref.role;
|
|
64
|
-
if (role === 'menu') {
|
|
65
|
-
return 'menu';
|
|
66
|
-
}
|
|
67
|
-
if (role === 'listbox') {
|
|
68
|
-
return 'listbox';
|
|
69
|
-
}
|
|
70
|
-
if (role === 'tree') {
|
|
71
|
-
return 'tree';
|
|
72
|
-
}
|
|
73
|
-
if (role === 'grid') {
|
|
74
|
-
return 'grid';
|
|
75
|
-
}
|
|
76
|
-
if (role === 'dialog' || role === 'alertdialog') {
|
|
77
|
-
return 'dialog';
|
|
78
|
-
}
|
|
79
|
-
// Non-popup roles (tooltip, note, status, alert, log) and the
|
|
80
|
-
// no-role case: omit `aria-haspopup` entirely. React drops the
|
|
81
|
-
// attribute when the value is `undefined`.
|
|
82
|
-
return undefined;
|
|
83
|
-
}
|
|
84
|
-
var rolesThatMoveFocus = new Set(['dialog', 'alertdialog', 'menu', 'listbox', 'tree', 'grid']);
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Returns `true` when the given popover role moves focus into the popover
|
|
88
|
-
* on open, and `false` otherwise (including when `role` is `undefined`).
|
|
89
|
-
*/
|
|
90
|
-
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
|
|
91
|
-
function shouldFocusIntoPopover(_ref2) {
|
|
92
|
-
var role = _ref2.role;
|
|
93
|
-
if (!role) {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
return rolesThatMoveFocus.has(role);
|
|
97
|
-
}
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.shouldFocusIntoPopover = shouldFocusIntoPopover;
|
|
7
|
+
var rolesThatMoveFocus = new Set(['dialog', 'alertdialog', 'menu', 'listbox', 'tree', 'grid']);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Returns `true` when the given popover role moves focus into the popover
|
|
11
|
+
* on open, and `false` otherwise (including when `role` is `undefined`).
|
|
12
|
+
*/
|
|
13
|
+
function shouldFocusIntoPopover(_ref) {
|
|
14
|
+
var role = _ref.role;
|
|
15
|
+
if (!role) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return rolesThatMoveFocus.has(role);
|
|
19
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useAnchorPositionAtPoint = useAnchorPositionAtPoint;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
var _useAnchorPosition = require("./use-anchor-position");
|
|
9
|
+
/**
|
|
10
|
+
* A point in viewport coordinates (CSS pixels) at which the synthetic
|
|
11
|
+
* anchor should be placed.
|
|
12
|
+
*
|
|
13
|
+
* Axis-named (`x`/`y`) rather than edge-named (`left`/`top`) so the
|
|
14
|
+
* shape is unambiguous in RTL writing modes — there is no ambiguity
|
|
15
|
+
* about whether a coordinate is logical (`inline-start`/`block-start`)
|
|
16
|
+
* or physical (`left`/`top`). Viewport coordinates are always
|
|
17
|
+
* physical, so we name them with the physical axis labels.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Styles the synthetic anchor as an invisible, zero-size point.
|
|
22
|
+
* Dynamic `inset-*` overrides are written by the positioning effect.
|
|
23
|
+
*
|
|
24
|
+
* Uses `Object.assign` rather than Compiled CSS (element is outside the React tree)
|
|
25
|
+
* or the internal `setStyle` helper (we own this element entirely — it is created
|
|
26
|
+
* and removed by this hook — so the snapshot/restore semantics `setStyle`
|
|
27
|
+
* provides for caller-owned elements would be dead work here).
|
|
28
|
+
*/
|
|
29
|
+
function applyStaticAnchorStyles(el) {
|
|
30
|
+
Object.assign(el.style, {
|
|
31
|
+
position: 'fixed',
|
|
32
|
+
// `top`/`left` rather than `inset-block-start`/`inset-inline-start`
|
|
33
|
+
// for consistency with the dynamic positioning effect below, which
|
|
34
|
+
// uses physical properties (see comment there for the rationale).
|
|
35
|
+
top: '0',
|
|
36
|
+
left: '0',
|
|
37
|
+
// Zero size — placement decides which side the popover sits on.
|
|
38
|
+
width: '0',
|
|
39
|
+
height: '0',
|
|
40
|
+
// Prevent the overlay from intercepting pointer events.
|
|
41
|
+
pointerEvents: 'none'
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// `useAnchorPosition` requires a real DOM element. When the caller only
|
|
46
|
+
// has a viewport coordinate (e.g. cursor at hover time), this hook
|
|
47
|
+
// manages a hidden `<div>` at that point and delegates the rest.
|
|
48
|
+
//
|
|
49
|
+
// The `<div>` is appended directly to `document.body` to escape any
|
|
50
|
+
// transformed ancestor, which would otherwise break `position: fixed`
|
|
51
|
+
// viewport-relative resolution.
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Positions a popover at a consumer-supplied viewport coordinate using a
|
|
55
|
+
* hidden synthetic anchor.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```tsx
|
|
59
|
+
* function PointPositionedPopover({ popoverRef, placement, mousePos }) {
|
|
60
|
+
* useAnchorPositionAtPoint({
|
|
61
|
+
* popoverRef,
|
|
62
|
+
* placement,
|
|
63
|
+
* isEnabled: Boolean(mousePos),
|
|
64
|
+
* getPoint: () => {
|
|
65
|
+
* if (!mousePos) return null;
|
|
66
|
+
* return { x: mousePos.clientX, y: mousePos.clientY };
|
|
67
|
+
* },
|
|
68
|
+
* });
|
|
69
|
+
* return <Popover ref={popoverRef}>...</Popover>;
|
|
70
|
+
* }
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
function useAnchorPositionAtPoint(_ref) {
|
|
74
|
+
var popoverRef = _ref.popoverRef,
|
|
75
|
+
placement = _ref.placement,
|
|
76
|
+
getPoint = _ref.getPoint,
|
|
77
|
+
_ref$isEnabled = _ref.isEnabled,
|
|
78
|
+
isEnabled = _ref$isEnabled === void 0 ? true : _ref$isEnabled;
|
|
79
|
+
var syntheticAnchorRef = (0, _react.useRef)(null);
|
|
80
|
+
|
|
81
|
+
// `getPoint` is kept in a ref so the layout effect can call the
|
|
82
|
+
// latest version without listing it as a dep — that would re-fire
|
|
83
|
+
// on every render and re-latch the point.
|
|
84
|
+
var getPointRef = (0, _react.useRef)(getPoint);
|
|
85
|
+
getPointRef.current = getPoint;
|
|
86
|
+
|
|
87
|
+
// Lazy element creation: the `<div>` is only created when both
|
|
88
|
+
// `isEnabled` is true AND `getPoint()` returns a non-null point.
|
|
89
|
+
// `useLayoutEffect` ensures the element exists before the inner
|
|
90
|
+
// `useAnchorPosition` (also a layout effect) reads the ref.
|
|
91
|
+
(0, _react.useLayoutEffect)(function () {
|
|
92
|
+
if (!isEnabled) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
var point = getPointRef.current();
|
|
96
|
+
if (!point) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
var anchor = document.createElement('div');
|
|
100
|
+
anchor.setAttribute('aria-hidden', 'true');
|
|
101
|
+
applyStaticAnchorStyles(anchor);
|
|
102
|
+
// `point.x`/`y` are physical viewport coordinates (from
|
|
103
|
+
// `MouseEvent.clientX/Y`), so map directly onto physical
|
|
104
|
+
// `left`/`top`. This is RTL-safe: `position: fixed` + physical
|
|
105
|
+
// insets is direction-independent. Using logical
|
|
106
|
+
// `inset-inline-start` would need `viewportWidth - point.x` in
|
|
107
|
+
// RTL to land at the same physical pixel.
|
|
108
|
+
anchor.style.top = "".concat(point.y, "px");
|
|
109
|
+
anchor.style.left = "".concat(point.x, "px");
|
|
110
|
+
document.body.appendChild(anchor);
|
|
111
|
+
syntheticAnchorRef.current = anchor;
|
|
112
|
+
return function cleanup() {
|
|
113
|
+
anchor.remove();
|
|
114
|
+
syntheticAnchorRef.current = null;
|
|
115
|
+
};
|
|
116
|
+
}, [isEnabled]);
|
|
117
|
+
|
|
118
|
+
// Mirror `isEnabled` so both hooks stay in sync — when disabled,
|
|
119
|
+
// neither this hook nor the inner one touches the popover, leaving
|
|
120
|
+
// any other positioning the consumer has set up free to take effect.
|
|
121
|
+
(0, _useAnchorPosition.useAnchorPosition)({
|
|
122
|
+
anchorRef: syntheticAnchorRef,
|
|
123
|
+
popoverRef: popoverRef,
|
|
124
|
+
placement: placement,
|
|
125
|
+
isEnabled: isEnabled
|
|
126
|
+
});
|
|
127
|
+
}
|
|
@@ -200,19 +200,23 @@ function useStablePlacement(placement) {
|
|
|
200
200
|
* comparison is used internally so re-runs are skipped when the resolved
|
|
201
201
|
* placement shape is unchanged.
|
|
202
202
|
*/
|
|
203
|
-
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
|
|
204
203
|
function useAnchorPosition(_ref3) {
|
|
205
204
|
var anchorRef = _ref3.anchorRef,
|
|
206
205
|
popoverRef = _ref3.popoverRef,
|
|
207
206
|
_ref3$placement = _ref3.placement,
|
|
208
207
|
placement = _ref3$placement === void 0 ? {} : _ref3$placement,
|
|
209
208
|
_ref3$forceFallbackPo = _ref3.forceFallbackPositioning,
|
|
210
|
-
forceFallbackPositioning = _ref3$forceFallbackPo === void 0 ? false : _ref3$forceFallbackPo
|
|
209
|
+
forceFallbackPositioning = _ref3$forceFallbackPo === void 0 ? false : _ref3$forceFallbackPo,
|
|
210
|
+
_ref3$isEnabled = _ref3.isEnabled,
|
|
211
|
+
isEnabled = _ref3$isEnabled === void 0 ? true : _ref3$isEnabled;
|
|
211
212
|
var id = (0, _react.useId)();
|
|
212
213
|
// Stabilize `placement` object so the same object literal
|
|
213
214
|
// value returns an object with the same reference
|
|
214
215
|
var stablePlacement = useStablePlacement(placement);
|
|
215
216
|
(0, _react.useLayoutEffect)(function () {
|
|
217
|
+
if (!isEnabled) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
216
220
|
var trigger = anchorRef.current;
|
|
217
221
|
var popover = popoverRef.current;
|
|
218
222
|
if (!trigger || !popover) {
|
|
@@ -461,5 +465,5 @@ function useAnchorPosition(_ref3) {
|
|
|
461
465
|
popover.style.removeProperty('opacity');
|
|
462
466
|
});
|
|
463
467
|
return undoPositioning;
|
|
464
|
-
}, [anchorRef, popoverRef, stablePlacement, forceFallbackPositioning, id]);
|
|
468
|
+
}, [anchorRef, popoverRef, stablePlacement, forceFallbackPositioning, isEnabled, id]);
|
|
465
469
|
}
|
|
@@ -6,7 +6,10 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.useFocusWrap = useFocusWrap;
|
|
7
7
|
var _react = require("react");
|
|
8
8
|
var _bindEventListener = require("bind-event-listener");
|
|
9
|
-
var
|
|
9
|
+
var _getFirstFocusable = require("../focus/get-first-focusable");
|
|
10
|
+
var _getLastFocusable = require("../focus/get-last-focusable");
|
|
11
|
+
var _getNextFocusable = require("../focus/get-next-focusable");
|
|
12
|
+
var _isNestedLayerFocused = require("../focus/is-nested-layer-focused");
|
|
10
13
|
/**
|
|
11
14
|
* Roles that require focus wrapping per WAI-ARIA APG.
|
|
12
15
|
*
|
|
@@ -62,7 +65,7 @@ function useFocusWrap(_ref) {
|
|
|
62
65
|
|
|
63
66
|
// If focus is already in a nested layer, then
|
|
64
67
|
// we continue to let that nested layer own focus
|
|
65
|
-
if ((0,
|
|
68
|
+
if ((0, _isNestedLayerFocused.isNestedLayerFocused)({
|
|
66
69
|
container: element
|
|
67
70
|
})) {
|
|
68
71
|
return;
|
|
@@ -75,7 +78,7 @@ function useFocusWrap(_ref) {
|
|
|
75
78
|
|
|
76
79
|
// Try to move to the next/previous focusable element relative
|
|
77
80
|
// to the currently focused element within the container.
|
|
78
|
-
var next = (0,
|
|
81
|
+
var next = (0, _getNextFocusable.getNextFocusable)({
|
|
79
82
|
container: element,
|
|
80
83
|
direction: direction
|
|
81
84
|
});
|
|
@@ -87,9 +90,9 @@ function useFocusWrap(_ref) {
|
|
|
87
90
|
// Nothing is currently focused inside the container (or the
|
|
88
91
|
// focused element is not in the focusable list). Fall back to
|
|
89
92
|
// the first or last focusable element depending on direction.
|
|
90
|
-
var fallback = direction === 'forwards' ? (0,
|
|
93
|
+
var fallback = direction === 'forwards' ? (0, _getFirstFocusable.getFirstFocusable)({
|
|
91
94
|
container: element
|
|
92
|
-
}) : (0,
|
|
95
|
+
}) : (0, _getLastFocusable.getLastFocusable)({
|
|
93
96
|
container: element
|
|
94
97
|
});
|
|
95
98
|
if (fallback) {
|
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.useInitialFocus = useInitialFocus;
|
|
7
7
|
var _react = require("react");
|
|
8
|
-
var
|
|
8
|
+
var _getFirstFocusable = require("../focus/get-first-focusable");
|
|
9
9
|
/**
|
|
10
10
|
* Determines the correct element to receive initial focus within a popover,
|
|
11
11
|
* based on its ARIA role.
|
|
@@ -33,7 +33,7 @@ function getInitialFocusTarget(_ref) {
|
|
|
33
33
|
return autofocusEl;
|
|
34
34
|
}
|
|
35
35
|
// Fall back to first focusable element
|
|
36
|
-
return (0,
|
|
36
|
+
return (0, _getFirstFocusable.getFirstFocusable)({
|
|
37
37
|
container: container
|
|
38
38
|
});
|
|
39
39
|
}
|
|
@@ -44,7 +44,7 @@ function getInitialFocusTarget(_ref) {
|
|
|
44
44
|
// of the consumer component (e.g. dropdown-menu), because menu keyboard
|
|
45
45
|
// behavior varies by context (orientation, nesting, item type, control
|
|
46
46
|
// pattern). See: notes/outputs/menu-keyboard-decision.md
|
|
47
|
-
return (0,
|
|
47
|
+
return (0, _getFirstFocusable.getFirstFocusable)({
|
|
48
48
|
container: container
|
|
49
49
|
});
|
|
50
50
|
}
|
|
@@ -54,7 +54,7 @@ function getInitialFocusTarget(_ref) {
|
|
|
54
54
|
if (selectedOption) {
|
|
55
55
|
return selectedOption;
|
|
56
56
|
}
|
|
57
|
-
return (0,
|
|
57
|
+
return (0, _getFirstFocusable.getFirstFocusable)({
|
|
58
58
|
container: container
|
|
59
59
|
});
|
|
60
60
|
}
|
|
@@ -7,7 +7,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.fromLegacyPlacement = fromLegacyPlacement;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
9
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
10
|
-
var
|
|
10
|
+
var _placementMapping = require("./placement-mapping");
|
|
11
11
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
12
12
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
13
13
|
/**
|
|
@@ -54,7 +54,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
54
54
|
function fromLegacyPlacement(_ref) {
|
|
55
55
|
var legacy = _ref.legacy,
|
|
56
56
|
offset = _ref.offset;
|
|
57
|
-
var basePlacement =
|
|
57
|
+
var basePlacement = _placementMapping.placementMapping[legacy];
|
|
58
58
|
if (!offset) {
|
|
59
59
|
return basePlacement;
|
|
60
60
|
}
|