@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
@@ -1,90 +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(_ref) {
56
- var role = _ref.role;
57
- if (role === 'menu') {
58
- return 'menu';
59
- }
60
- if (role === 'listbox') {
61
- return 'listbox';
62
- }
63
- if (role === 'tree') {
64
- return 'tree';
65
- }
66
- if (role === 'grid') {
67
- return 'grid';
68
- }
69
- if (role === 'dialog' || role === 'alertdialog') {
70
- return 'dialog';
71
- }
72
- // Non-popup roles (tooltip, note, status, alert, log) and the
73
- // no-role case: omit `aria-haspopup` entirely. React drops the
74
- // attribute when the value is `undefined`.
75
- return undefined;
76
- }
77
- var rolesThatMoveFocus = new Set(['dialog', 'alertdialog', 'menu', 'listbox', 'tree', 'grid']);
78
-
79
- /**
80
- * Returns `true` when the given popover role moves focus into the popover
81
- * on open, and `false` otherwise (including when `role` is `undefined`).
82
- */
83
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
84
- export function shouldFocusIntoPopover(_ref2) {
85
- var role = _ref2.role;
86
- if (!role) {
87
- return false;
88
- }
89
- return rolesThatMoveFocus.has(role);
90
- }
@@ -0,0 +1,13 @@
1
+ var 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(_ref) {
8
+ var role = _ref.role;
9
+ if (!role) {
10
+ return false;
11
+ }
12
+ return rolesThatMoveFocus.has(role);
13
+ }
@@ -0,0 +1,122 @@
1
+ import { useLayoutEffect, useRef } from 'react';
2
+ import { useAnchorPosition } from './use-anchor-position';
3
+
4
+ /**
5
+ * A point in viewport coordinates (CSS pixels) at which the synthetic
6
+ * anchor should be placed.
7
+ *
8
+ * Axis-named (`x`/`y`) rather than edge-named (`left`/`top`) so the
9
+ * shape is unambiguous in RTL writing modes — there is no ambiguity
10
+ * about whether a coordinate is logical (`inline-start`/`block-start`)
11
+ * or physical (`left`/`top`). Viewport coordinates are always
12
+ * physical, so we name them with the physical axis labels.
13
+ */
14
+
15
+ /**
16
+ * Styles the synthetic anchor as an invisible, zero-size point.
17
+ * Dynamic `inset-*` overrides are written by the positioning effect.
18
+ *
19
+ * Uses `Object.assign` rather than Compiled CSS (element is outside the React tree)
20
+ * or the internal `setStyle` helper (we own this element entirely — it is created
21
+ * and removed by this hook — so the snapshot/restore semantics `setStyle`
22
+ * provides for caller-owned elements would be dead work here).
23
+ */
24
+ function applyStaticAnchorStyles(el) {
25
+ Object.assign(el.style, {
26
+ position: 'fixed',
27
+ // `top`/`left` rather than `inset-block-start`/`inset-inline-start`
28
+ // for consistency with the dynamic positioning effect below, which
29
+ // uses physical properties (see comment there for the rationale).
30
+ top: '0',
31
+ left: '0',
32
+ // Zero size — placement decides which side the popover sits on.
33
+ width: '0',
34
+ height: '0',
35
+ // Prevent the overlay from intercepting pointer events.
36
+ pointerEvents: 'none'
37
+ });
38
+ }
39
+
40
+ // `useAnchorPosition` requires a real DOM element. When the caller only
41
+ // has a viewport coordinate (e.g. cursor at hover time), this hook
42
+ // manages a hidden `<div>` at that point and delegates the rest.
43
+ //
44
+ // The `<div>` is appended directly to `document.body` to escape any
45
+ // transformed ancestor, which would otherwise break `position: fixed`
46
+ // viewport-relative resolution.
47
+
48
+ /**
49
+ * Positions a popover at a consumer-supplied viewport coordinate using a
50
+ * hidden synthetic anchor.
51
+ *
52
+ * @example
53
+ * ```tsx
54
+ * function PointPositionedPopover({ popoverRef, placement, mousePos }) {
55
+ * useAnchorPositionAtPoint({
56
+ * popoverRef,
57
+ * placement,
58
+ * isEnabled: Boolean(mousePos),
59
+ * getPoint: () => {
60
+ * if (!mousePos) return null;
61
+ * return { x: mousePos.clientX, y: mousePos.clientY };
62
+ * },
63
+ * });
64
+ * return <Popover ref={popoverRef}>...</Popover>;
65
+ * }
66
+ * ```
67
+ */
68
+ export function useAnchorPositionAtPoint(_ref) {
69
+ var popoverRef = _ref.popoverRef,
70
+ placement = _ref.placement,
71
+ getPoint = _ref.getPoint,
72
+ _ref$isEnabled = _ref.isEnabled,
73
+ isEnabled = _ref$isEnabled === void 0 ? true : _ref$isEnabled;
74
+ var syntheticAnchorRef = useRef(null);
75
+
76
+ // `getPoint` is kept in a ref so the layout effect can call the
77
+ // latest version without listing it as a dep — that would re-fire
78
+ // on every render and re-latch the point.
79
+ var getPointRef = useRef(getPoint);
80
+ getPointRef.current = getPoint;
81
+
82
+ // Lazy element creation: the `<div>` is only created when both
83
+ // `isEnabled` is true AND `getPoint()` returns a non-null point.
84
+ // `useLayoutEffect` ensures the element exists before the inner
85
+ // `useAnchorPosition` (also a layout effect) reads the ref.
86
+ useLayoutEffect(function () {
87
+ if (!isEnabled) {
88
+ return;
89
+ }
90
+ var point = getPointRef.current();
91
+ if (!point) {
92
+ return;
93
+ }
94
+ var anchor = document.createElement('div');
95
+ anchor.setAttribute('aria-hidden', 'true');
96
+ applyStaticAnchorStyles(anchor);
97
+ // `point.x`/`y` are physical viewport coordinates (from
98
+ // `MouseEvent.clientX/Y`), so map directly onto physical
99
+ // `left`/`top`. This is RTL-safe: `position: fixed` + physical
100
+ // insets is direction-independent. Using logical
101
+ // `inset-inline-start` would need `viewportWidth - point.x` in
102
+ // RTL to land at the same physical pixel.
103
+ anchor.style.top = "".concat(point.y, "px");
104
+ anchor.style.left = "".concat(point.x, "px");
105
+ document.body.appendChild(anchor);
106
+ syntheticAnchorRef.current = anchor;
107
+ return function cleanup() {
108
+ anchor.remove();
109
+ syntheticAnchorRef.current = null;
110
+ };
111
+ }, [isEnabled]);
112
+
113
+ // Mirror `isEnabled` so both hooks stay in sync — when disabled,
114
+ // neither this hook nor the inner one touches the popover, leaving
115
+ // any other positioning the consumer has set up free to take effect.
116
+ useAnchorPosition({
117
+ anchorRef: syntheticAnchorRef,
118
+ popoverRef: popoverRef,
119
+ placement: placement,
120
+ isEnabled: isEnabled
121
+ });
122
+ }
@@ -193,19 +193,23 @@ function useStablePlacement(placement) {
193
193
  * comparison is used internally so re-runs are skipped when the resolved
194
194
  * placement shape is unchanged.
195
195
  */
196
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
197
196
  export function useAnchorPosition(_ref3) {
198
197
  var anchorRef = _ref3.anchorRef,
199
198
  popoverRef = _ref3.popoverRef,
200
199
  _ref3$placement = _ref3.placement,
201
200
  placement = _ref3$placement === void 0 ? {} : _ref3$placement,
202
201
  _ref3$forceFallbackPo = _ref3.forceFallbackPositioning,
203
- forceFallbackPositioning = _ref3$forceFallbackPo === void 0 ? false : _ref3$forceFallbackPo;
202
+ forceFallbackPositioning = _ref3$forceFallbackPo === void 0 ? false : _ref3$forceFallbackPo,
203
+ _ref3$isEnabled = _ref3.isEnabled,
204
+ isEnabled = _ref3$isEnabled === void 0 ? true : _ref3$isEnabled;
204
205
  var id = useId();
205
206
  // Stabilize `placement` object so the same object literal
206
207
  // value returns an object with the same reference
207
208
  var stablePlacement = useStablePlacement(placement);
208
209
  useLayoutEffect(function () {
210
+ if (!isEnabled) {
211
+ return;
212
+ }
209
213
  var trigger = anchorRef.current;
210
214
  var popover = popoverRef.current;
211
215
  if (!trigger || !popover) {
@@ -454,5 +458,5 @@ export function useAnchorPosition(_ref3) {
454
458
  popover.style.removeProperty('opacity');
455
459
  });
456
460
  return undoPositioning;
457
- }, [anchorRef, popoverRef, stablePlacement, forceFallbackPositioning, id]);
461
+ }, [anchorRef, popoverRef, stablePlacement, forceFallbackPositioning, isEnabled, id]);
458
462
  }
@@ -1,6 +1,9 @@
1
1
  import { useEffect } from 'react';
2
2
  import { bind } from 'bind-event-listener';
3
- import { getFirstFocusable, getLastFocusable, getNextFocusable, isNestedLayerFocused } from '../focus/focus';
3
+ import { getFirstFocusable } from '../focus/get-first-focusable';
4
+ import { getLastFocusable } from '../focus/get-last-focusable';
5
+ import { getNextFocusable } from '../focus/get-next-focusable';
6
+ import { isNestedLayerFocused } from '../focus/is-nested-layer-focused';
4
7
 
5
8
  /**
6
9
  * Roles that require focus wrapping per WAI-ARIA APG.
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useRef } from 'react';
2
- import { getFirstFocusable } from '../focus/focus';
2
+ import { getFirstFocusable } from '../focus/get-first-focusable';
3
3
 
4
4
  /**
5
5
  * Determines the correct element to receive initial focus within a popover,
@@ -2,7 +2,7 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
2
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
3
  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; }
4
4
  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) { _defineProperty(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; }
5
- import { placementMapping } from './legacy-placements';
5
+ import { placementMapping } from './placement-mapping';
6
6
 
7
7
  /**
8
8
  * Maps legacy Popper.js placement values to the new object-based
@@ -5,103 +5,4 @@
5
5
  * to enumerate every placement (e.g. for a settings dropdown). New code
6
6
  * should use the object-based `TPlacementOptions` directly.
7
7
  */
8
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
9
- export var 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'];
10
- /**
11
- * The full mapping record. Internal - use `fromLegacyPlacement` from the
12
- * public entry-point so the offset-tuple translation is applied consistently.
13
- *
14
- * Note: `auto`, `auto-start`, `auto-end` map to `block-end` placements. This
15
- * is a reduction in expressiveness vs Popper.js (which picked the best
16
- * placement at runtime). CSS `position-try-fallbacks` flips at the edge but
17
- * the initial placement is fixed. Migration consumers who relied on Popper's
18
- * dynamic auto-flip should pick an explicit placement instead.
19
- */
20
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
21
- export var placementMapping = {
22
- top: {
23
- axis: 'block',
24
- edge: 'start',
25
- align: 'center'
26
- },
27
- 'top-start': {
28
- axis: 'block',
29
- edge: 'start',
30
- align: 'start'
31
- },
32
- 'top-center': {
33
- axis: 'block',
34
- edge: 'start',
35
- align: 'center'
36
- },
37
- 'top-end': {
38
- axis: 'block',
39
- edge: 'start',
40
- align: 'end'
41
- },
42
- bottom: {
43
- axis: 'block',
44
- edge: 'end',
45
- align: 'center'
46
- },
47
- 'bottom-start': {
48
- axis: 'block',
49
- edge: 'end',
50
- align: 'start'
51
- },
52
- 'bottom-center': {
53
- axis: 'block',
54
- edge: 'end',
55
- align: 'center'
56
- },
57
- 'bottom-end': {
58
- axis: 'block',
59
- edge: 'end',
60
- align: 'end'
61
- },
62
- right: {
63
- axis: 'inline',
64
- edge: 'end',
65
- align: 'center'
66
- },
67
- 'right-start': {
68
- axis: 'inline',
69
- edge: 'end',
70
- align: 'start'
71
- },
72
- 'right-end': {
73
- axis: 'inline',
74
- edge: 'end',
75
- align: 'end'
76
- },
77
- left: {
78
- axis: 'inline',
79
- edge: 'start',
80
- align: 'center'
81
- },
82
- 'left-start': {
83
- axis: 'inline',
84
- edge: 'start',
85
- align: 'start'
86
- },
87
- 'left-end': {
88
- axis: 'inline',
89
- edge: 'start',
90
- align: 'end'
91
- },
92
- auto: {
93
- axis: 'block',
94
- edge: 'end',
95
- align: 'center'
96
- },
97
- 'auto-start': {
98
- axis: 'block',
99
- edge: 'end',
100
- align: 'start'
101
- },
102
- 'auto-end': {
103
- axis: 'block',
104
- edge: 'end',
105
- align: 'end'
106
- }
107
- };
8
+ export var 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,97 @@
1
+ /**
2
+ * The full mapping record. Internal - use `fromLegacyPlacement` from the
3
+ * public entry-point so the offset-tuple translation is applied consistently.
4
+ *
5
+ * Note: `auto`, `auto-start`, `auto-end` map to `block-end` placements. This
6
+ * is a reduction in expressiveness vs Popper.js (which picked the best
7
+ * placement at runtime). CSS `position-try-fallbacks` flips at the edge but
8
+ * the initial placement is fixed. Migration consumers who relied on Popper's
9
+ * dynamic auto-flip should pick an explicit placement instead.
10
+ */
11
+ export var placementMapping = {
12
+ top: {
13
+ axis: 'block',
14
+ edge: 'start',
15
+ align: 'center'
16
+ },
17
+ 'top-start': {
18
+ axis: 'block',
19
+ edge: 'start',
20
+ align: 'start'
21
+ },
22
+ 'top-center': {
23
+ axis: 'block',
24
+ edge: 'start',
25
+ align: 'center'
26
+ },
27
+ 'top-end': {
28
+ axis: 'block',
29
+ edge: 'start',
30
+ align: 'end'
31
+ },
32
+ bottom: {
33
+ axis: 'block',
34
+ edge: 'end',
35
+ align: 'center'
36
+ },
37
+ 'bottom-start': {
38
+ axis: 'block',
39
+ edge: 'end',
40
+ align: 'start'
41
+ },
42
+ 'bottom-center': {
43
+ axis: 'block',
44
+ edge: 'end',
45
+ align: 'center'
46
+ },
47
+ 'bottom-end': {
48
+ axis: 'block',
49
+ edge: 'end',
50
+ align: 'end'
51
+ },
52
+ right: {
53
+ axis: 'inline',
54
+ edge: 'end',
55
+ align: 'center'
56
+ },
57
+ 'right-start': {
58
+ axis: 'inline',
59
+ edge: 'end',
60
+ align: 'start'
61
+ },
62
+ 'right-end': {
63
+ axis: 'inline',
64
+ edge: 'end',
65
+ align: 'end'
66
+ },
67
+ left: {
68
+ axis: 'inline',
69
+ edge: 'start',
70
+ align: 'center'
71
+ },
72
+ 'left-start': {
73
+ axis: 'inline',
74
+ edge: 'start',
75
+ align: 'start'
76
+ },
77
+ 'left-end': {
78
+ axis: 'inline',
79
+ edge: 'start',
80
+ align: 'end'
81
+ },
82
+ auto: {
83
+ axis: 'block',
84
+ edge: 'end',
85
+ align: 'center'
86
+ },
87
+ 'auto-start': {
88
+ axis: 'block',
89
+ edge: 'end',
90
+ align: 'start'
91
+ },
92
+ 'auto-end': {
93
+ axis: 'block',
94
+ edge: 'end',
95
+ align: 'end'
96
+ }
97
+ };
@@ -4,7 +4,8 @@ import { ax, ix } from "@compiled/react/runtime";
4
4
  import React, { forwardRef, useCallback, useLayoutEffect, useRef } from 'react';
5
5
  import mergeRefs from '@atlaskit/ds-lib/merge-refs';
6
6
  import { prefersReducedMotion } from '../internal/reduced-motion';
7
- import { roleToAriaHasPopup, shouldFocusIntoPopover } from '../internal/role-types';
7
+ import { roleToAriaHasPopup } from '../internal/role-to-aria-has-popup';
8
+ import { shouldFocusIntoPopover } from '../internal/should-focus-into-popover';
8
9
  import { useAnchorPosition } from '../internal/use-anchor-position';
9
10
  import { useWidthFromAnchor } from '../internal/use-width-from-anchor';
10
11
  import { Popover } from '../popover/popover';
@@ -2,7 +2,9 @@ import { useCallback, useEffect } from 'react';
2
2
  import { bind } from 'bind-event-listener';
3
3
  import { getDocument } from '@atlaskit/browser-apis';
4
4
  import { KEY_DOWN, KEY_END, KEY_HOME, KEY_TAB, KEY_UP } from '@atlaskit/ds-lib/keycodes';
5
- import { getFirstFocusable, getLastFocusable, getNextFocusable } from '../focus';
5
+ import { getFirstFocusable } from '../focus/get-first-focusable';
6
+ import { getLastFocusable } from '../focus/get-last-focusable';
7
+ import { getNextFocusable } from '../focus/get-next-focusable';
6
8
  var menuScope = '[role="menu"]';
7
9
 
8
10
  /**
@@ -1,2 +1,4 @@
1
- export { getFirstFocusable, getLastFocusable, getNextFocusable } from '../focus';
2
- export type { TFocusableFilter } from '../focus';
1
+ export type { TFocusableFilter } from '../focus/types';
2
+ export { getFirstFocusable } from '../focus/get-first-focusable';
3
+ export { getLastFocusable } from '../focus/get-last-focusable';
4
+ export { getNextFocusable } from '../focus/get-next-focusable';
@@ -0,0 +1 @@
1
+ export { useAnchorPositionAtPoint, type TAnchorPoint, } from '../internal/use-anchor-position-at-point';
@@ -0,0 +1,8 @@
1
+ import type { TFocusableFilter } from './types';
2
+ /**
3
+ * Returns the first focusable element within the container.
4
+ */
5
+ export declare function getFirstFocusable({ container, filter, }: {
6
+ container: HTMLElement;
7
+ filter?: TFocusableFilter;
8
+ }): HTMLElement | null;
@@ -0,0 +1,13 @@
1
+ import type { TFocusableFilter } from './types';
2
+ /**
3
+ * Returns all focusable HTMLElements within the container, optionally filtered.
4
+ *
5
+ * Focusables that belong to a nested top-layer scope (a `[popover]` or
6
+ * `<dialog>` descendant of the container) are excluded - those elements
7
+ * are owned by the inner layer's focus management. The container itself
8
+ * is allowed to be a popover/dialog; only nested ones are filtered.
9
+ */
10
+ export declare function getFocusables({ container, filter, }: {
11
+ container: HTMLElement;
12
+ filter?: TFocusableFilter;
13
+ }): HTMLElement[];
@@ -0,0 +1,8 @@
1
+ import type { TFocusableFilter } from './types';
2
+ /**
3
+ * Returns the last focusable element within the container.
4
+ */
5
+ export declare function getLastFocusable({ container, filter, }: {
6
+ container: HTMLElement;
7
+ filter?: TFocusableFilter;
8
+ }): HTMLElement | null;
@@ -0,0 +1,13 @@
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
+ import type { TFocusableFilter } from './types';
9
+ export declare function getNextFocusable({ container, direction, filter, }: {
10
+ container: HTMLElement;
11
+ direction: 'forwards' | 'backwards';
12
+ filter?: TFocusableFilter;
13
+ }): HTMLElement | null;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Returns true when `element` and `container` resolve to the same nearest
3
+ * top-layer host (or both resolve to `null`, meaning the document layer).
4
+ */
5
+ export declare function isInSameLayer({ element, container, }: {
6
+ element: HTMLElement;
7
+ container: HTMLElement;
8
+ }): boolean;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Returns `true` when `document.activeElement` is inside a nested top-layer
3
+ * descendant of `container` (a `[popover]`, `<dialog>`, `[role="dialog"]` or
4
+ * `[role="alertdialog"]` that is itself a descendant of `container`).
5
+ */
6
+ export declare function isNestedLayerFocused({ container }: {
7
+ container: HTMLElement;
8
+ }): boolean;
@@ -0,0 +1,4 @@
1
+ export declare const selectors: {
2
+ focusable: string;
3
+ focused: string;
4
+ };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * An optional callback to restrict which focusable elements are included.
3
+ * Return `true` to include the element, `false` to skip it.
4
+ */
5
+ export type TFocusableFilter = (element: HTMLElement, container: HTMLElement) => boolean;
@@ -0,0 +1,13 @@
1
+ import type { TAriaHasPopupValue, TRoleRequiringAccessibleName, TRoleWithImplicitName } from './role-types';
2
+ /**
3
+ * Maps a popover content role to the correct `aria-haspopup` value
4
+ * for its trigger element.
5
+ *
6
+ * Returns `undefined` for roles that are not popups in the ARIA sense
7
+ * (`tooltip`, `note`, `status`, `alert`, `log`). Triggers for those roles
8
+ * should not announce a popup at all - emitting `aria-haspopup="dialog"`
9
+ * on a tooltip trigger would mislead assistive technology.
10
+ */
11
+ export declare function roleToAriaHasPopup({ role, }: {
12
+ role: TRoleRequiringAccessibleName | TRoleWithImplicitName | undefined;
13
+ }): TAriaHasPopupValue;
@@ -61,23 +61,4 @@ export type TAriaRoleOptional = ({
61
61
  } & TAccessibleNameRequired) | ({
62
62
  role?: TRoleWithImplicitName;
63
63
  } & TAccessibleNameOptional);
64
- /**
65
- * Maps a popover content role to the correct `aria-haspopup` value
66
- * for its trigger element.
67
- *
68
- * Returns `undefined` for roles that are not popups in the ARIA sense
69
- * (`tooltip`, `note`, `status`, `alert`, `log`). Triggers for those roles
70
- * should not announce a popup at all - emitting `aria-haspopup="dialog"`
71
- * on a tooltip trigger would mislead assistive technology.
72
- */
73
- export declare function roleToAriaHasPopup({ role, }: {
74
- role: TRoleRequiringAccessibleName | TRoleWithImplicitName | undefined;
75
- }): TAriaHasPopupValue;
76
- /**
77
- * Returns `true` when the given popover role moves focus into the popover
78
- * on open, and `false` otherwise (including when `role` is `undefined`).
79
- */
80
- export declare function shouldFocusIntoPopover({ role, }: {
81
- role: TRoleRequiringAccessibleName | TRoleWithImplicitName | undefined;
82
- }): boolean;
83
64
  export {};
@@ -0,0 +1,8 @@
1
+ import type { TRoleRequiringAccessibleName, TRoleWithImplicitName } from './role-types';
2
+ /**
3
+ * Returns `true` when the given popover role moves focus into the popover
4
+ * on open, and `false` otherwise (including when `role` is `undefined`).
5
+ */
6
+ export declare function shouldFocusIntoPopover({ role, }: {
7
+ role: TRoleRequiringAccessibleName | TRoleWithImplicitName | undefined;
8
+ }): boolean;