@atlaskit/top-layer 0.8.0 → 0.10.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 (130) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +5 -1
  3. package/dialog/create-close-event/package.json +17 -0
  4. package/dist/cjs/animations/presets.js +11 -4
  5. package/dist/cjs/arrow/index.js +131 -0
  6. package/dist/cjs/arrow/types.js +5 -0
  7. package/dist/cjs/dialog/dialog-content.js +23 -6
  8. package/dist/cjs/entry-points/dialog/create-close-event.js +12 -0
  9. package/dist/cjs/entry-points/placement-map.js +6 -5
  10. package/dist/cjs/entry-points/popover/create-close-event.js +12 -0
  11. package/dist/cjs/focus/focus.js +73 -19
  12. package/dist/cjs/index.js +3 -4
  13. package/dist/cjs/internal/resolve-css-length-to-pixels.js +61 -10
  14. package/dist/cjs/internal/role-types.js +40 -9
  15. package/dist/cjs/internal/set-style.js +23 -25
  16. package/dist/cjs/internal/use-anchor-position.js +109 -68
  17. package/dist/cjs/internal/use-focus-wrap.js +13 -2
  18. package/dist/cjs/internal/use-initial-focus.js +31 -10
  19. package/dist/cjs/placement-map/index.js +9 -110
  20. package/dist/cjs/placement-map/legacy-placements.js +113 -0
  21. package/dist/cjs/popover/popover.js +44 -11
  22. package/dist/cjs/popup/popup-content.js +19 -13
  23. package/dist/cjs/popup/popup-context.js +2 -0
  24. package/dist/cjs/popup/popup-surface.js +6 -0
  25. package/dist/cjs/popup/popup-trigger-function.js +1 -1
  26. package/dist/cjs/popup/popup-trigger.js +17 -37
  27. package/dist/cjs/popup/popup.js +21 -10
  28. package/dist/cjs/use-arrow-navigation/use-arrow-navigation.js +27 -10
  29. package/dist/es2019/animations/presets.js +10 -3
  30. package/dist/es2019/arrow/index.js +269 -0
  31. package/dist/es2019/arrow/types.js +1 -0
  32. package/dist/es2019/dialog/dialog-content.js +23 -6
  33. package/dist/es2019/entry-points/create-close-event.js +7 -0
  34. package/dist/es2019/entry-points/dialog/create-close-event.js +1 -0
  35. package/dist/es2019/entry-points/placement-map.js +2 -1
  36. package/dist/es2019/entry-points/popover/create-close-event.js +1 -0
  37. package/dist/es2019/focus/focus.js +58 -7
  38. package/dist/es2019/index.js +3 -4
  39. package/dist/es2019/internal/resolve-css-length-to-pixels.js +60 -7
  40. package/dist/es2019/internal/role-types.js +40 -9
  41. package/dist/es2019/internal/set-style.js +19 -22
  42. package/dist/es2019/internal/use-anchor-position.js +97 -54
  43. package/dist/es2019/internal/use-focus-wrap.js +14 -3
  44. package/dist/es2019/internal/use-initial-focus.js +32 -11
  45. package/dist/es2019/placement-map/index.js +8 -108
  46. package/dist/es2019/placement-map/legacy-placements.js +107 -0
  47. package/dist/es2019/popover/popover.js +44 -10
  48. package/dist/es2019/popup/popup-content.js +20 -14
  49. package/dist/es2019/popup/popup-context.js +2 -0
  50. package/dist/es2019/popup/popup-surface.js +6 -0
  51. package/dist/es2019/popup/popup-trigger-function.js +1 -1
  52. package/dist/es2019/popup/popup-trigger.js +17 -37
  53. package/dist/es2019/popup/popup.js +20 -9
  54. package/dist/es2019/use-arrow-navigation/use-arrow-navigation.js +27 -10
  55. package/dist/esm/animations/presets.js +10 -3
  56. package/dist/esm/arrow/index.js +124 -0
  57. package/dist/esm/arrow/types.js +1 -0
  58. package/dist/esm/dialog/dialog-content.js +23 -6
  59. package/dist/esm/entry-points/create-close-event.js +7 -0
  60. package/dist/esm/entry-points/dialog/create-close-event.js +1 -0
  61. package/dist/esm/entry-points/placement-map.js +2 -1
  62. package/dist/esm/entry-points/popover/create-close-event.js +1 -0
  63. package/dist/esm/focus/focus.js +72 -19
  64. package/dist/esm/index.js +3 -4
  65. package/dist/esm/internal/resolve-css-length-to-pixels.js +61 -10
  66. package/dist/esm/internal/role-types.js +39 -9
  67. package/dist/esm/internal/set-style.js +23 -25
  68. package/dist/esm/internal/use-anchor-position.js +110 -64
  69. package/dist/esm/internal/use-focus-wrap.js +14 -3
  70. package/dist/esm/internal/use-initial-focus.js +32 -11
  71. package/dist/esm/placement-map/index.js +8 -108
  72. package/dist/esm/placement-map/legacy-placements.js +107 -0
  73. package/dist/esm/popover/popover.js +44 -11
  74. package/dist/esm/popup/popup-content.js +20 -14
  75. package/dist/esm/popup/popup-context.js +2 -0
  76. package/dist/esm/popup/popup-surface.js +6 -0
  77. package/dist/esm/popup/popup-trigger-function.js +1 -1
  78. package/dist/esm/popup/popup-trigger.js +17 -37
  79. package/dist/esm/popup/popup.js +21 -10
  80. package/dist/esm/use-arrow-navigation/use-arrow-navigation.js +27 -10
  81. package/dist/types/arrow/index.d.ts +24 -0
  82. package/dist/types/arrow/types.d.ts +29 -0
  83. package/dist/types/dialog/dialog-content.d.ts +5 -1
  84. package/dist/types/dialog/types.d.ts +50 -24
  85. package/dist/types/entry-points/create-close-event.d.ts +7 -0
  86. package/dist/types/entry-points/dialog/create-close-event.d.ts +2 -0
  87. package/dist/types/entry-points/placement-map.d.ts +2 -1
  88. package/dist/types/entry-points/popover/create-close-event.d.ts +2 -0
  89. package/dist/types/focus/focus.d.ts +8 -0
  90. package/dist/types/internal/resolve-css-length-to-pixels.d.ts +4 -6
  91. package/dist/types/internal/role-types.d.ts +25 -4
  92. package/dist/types/internal/set-style.d.ts +2 -19
  93. package/dist/types/internal/use-anchor-position.d.ts +2 -3
  94. package/dist/types/internal/use-initial-focus.d.ts +5 -4
  95. package/dist/types/placement-map/index.d.ts +8 -21
  96. package/dist/types/placement-map/legacy-placements.d.ts +21 -0
  97. package/dist/types/popover/popover.d.ts +1 -1
  98. package/dist/types/popover/types.d.ts +20 -19
  99. package/dist/types/popup/popup-surface.d.ts +6 -0
  100. package/dist/types/popup/popup-trigger-function.d.ts +10 -6
  101. package/dist/types/popup/popup-trigger.d.ts +8 -17
  102. package/dist/types/popup/popup.d.ts +2 -2
  103. package/dist/types/popup/types.d.ts +68 -22
  104. package/dist/types/use-arrow-navigation/use-arrow-navigation.d.ts +19 -5
  105. package/dist/types-ts4.5/arrow/index.d.ts +24 -0
  106. package/dist/types-ts4.5/arrow/types.d.ts +29 -0
  107. package/dist/types-ts4.5/dialog/dialog-content.d.ts +5 -1
  108. package/dist/types-ts4.5/dialog/types.d.ts +50 -24
  109. package/dist/types-ts4.5/entry-points/create-close-event.d.ts +7 -0
  110. package/dist/types-ts4.5/entry-points/dialog/create-close-event.d.ts +2 -0
  111. package/dist/types-ts4.5/entry-points/placement-map.d.ts +2 -1
  112. package/dist/types-ts4.5/entry-points/popover/create-close-event.d.ts +2 -0
  113. package/dist/types-ts4.5/focus/focus.d.ts +8 -0
  114. package/dist/types-ts4.5/internal/resolve-css-length-to-pixels.d.ts +4 -6
  115. package/dist/types-ts4.5/internal/role-types.d.ts +25 -4
  116. package/dist/types-ts4.5/internal/set-style.d.ts +2 -19
  117. package/dist/types-ts4.5/internal/use-anchor-position.d.ts +2 -3
  118. package/dist/types-ts4.5/internal/use-initial-focus.d.ts +5 -4
  119. package/dist/types-ts4.5/placement-map/index.d.ts +8 -21
  120. package/dist/types-ts4.5/placement-map/legacy-placements.d.ts +39 -0
  121. package/dist/types-ts4.5/popover/popover.d.ts +1 -1
  122. package/dist/types-ts4.5/popover/types.d.ts +20 -19
  123. package/dist/types-ts4.5/popup/popup-surface.d.ts +6 -0
  124. package/dist/types-ts4.5/popup/popup-trigger-function.d.ts +10 -6
  125. package/dist/types-ts4.5/popup/popup-trigger.d.ts +8 -17
  126. package/dist/types-ts4.5/popup/popup.d.ts +2 -2
  127. package/dist/types-ts4.5/popup/types.d.ts +68 -22
  128. package/dist/types-ts4.5/use-arrow-navigation/use-arrow-navigation.d.ts +19 -5
  129. package/package.json +3 -1
  130. package/popover/create-close-event/package.json +17 -0
@@ -24,8 +24,8 @@ var _focus = require("../focus/focus");
24
24
  * React 19+ reflects the JSX prop as the HTML attribute automatically.
25
25
  */
26
26
  function getInitialFocusTarget(_ref) {
27
- var container = _ref.container;
28
- var role = container.getAttribute('role');
27
+ var container = _ref.container,
28
+ role = _ref.role;
29
29
  if (role === 'dialog' || role === 'alertdialog') {
30
30
  // Prefer element with the native HTML autofocus attribute.
31
31
  var autofocusEl = container.querySelector('[autofocus]');
@@ -66,14 +66,15 @@ function getInitialFocusTarget(_ref) {
66
66
  /**
67
67
  * Moves focus into a popover element when it opens, based on its ARIA role.
68
68
  *
69
- * This implements WCAG 2.4.3 (Focus Order) initial focus placement:
69
+ * Follows the WAI-ARIA APG initial-focus patterns for each role:
70
70
  * - `dialog` / `alertdialog` → focus first focusable (or `[autofocus]`)
71
71
  * - `menu` → focus first menu item
72
72
  * - `listbox` → focus selected or first option
73
- * - `tooltip` / no role → no focus movement
73
+ * - `tooltip` / no role → no focus movement (focus must remain on trigger;
74
+ * moving focus into the tooltip would dismiss it)
74
75
  *
75
- * The role is read from the element's `role` attribute (not passed as a prop),
76
- * following the same pattern as `useFocusWrap`.
76
+ * Role is taken from the typed `role` prop - this is the consumer's contract
77
+ * and is not subject to drift from a directly-mutated DOM attribute.
77
78
  *
78
79
  * Always call this hook unconditionally. Focus is only moved when `isOpen`
79
80
  * transitions to `true` and the role requires it.
@@ -82,6 +83,12 @@ function useInitialFocus(_ref2) {
82
83
  var elementRef = _ref2.elementRef,
83
84
  isOpen = _ref2.isOpen,
84
85
  role = _ref2.role;
86
+ // Track the most recently scheduled RAF so a rapid `isOpen → false → true`
87
+ // transition can cancel the previous frame before its closure runs against
88
+ // a stale popover element. Without this, the RAF queued during the first
89
+ // open could fire after the second open had begun and focus the wrong
90
+ // instance.
91
+ var pendingRafRef = (0, _react.useRef)(null);
85
92
  (0, _react.useEffect)(function () {
86
93
  if (!isOpen) {
87
94
  return;
@@ -91,6 +98,11 @@ function useInitialFocus(_ref2) {
91
98
  return;
92
99
  }
93
100
 
101
+ // Cancel any RAF queued by the previous open cycle.
102
+ if (pendingRafRef.current !== null) {
103
+ cancelAnimationFrame(pendingRafRef.current);
104
+ }
105
+
94
106
  // Use requestAnimationFrame to ensure the popover is fully rendered
95
107
  // and visible in the top layer before attempting to move focus.
96
108
  // showPopover() is called in useLayoutEffect (which runs before this
@@ -98,15 +110,24 @@ function useInitialFocus(_ref2) {
98
110
  // element is guaranteed to be in the top layer.
99
111
  var rafId = requestAnimationFrame(function () {
100
112
  var _getInitialFocusTarge;
113
+ pendingRafRef.current = null;
114
+ // Verify the element we captured is still the live popover. In
115
+ // rapid-toggle scenarios the ref may already point to a different
116
+ // instance - skip rather than focus the wrong popover.
117
+ if (elementRef.current !== el) {
118
+ return;
119
+ }
101
120
  (_getInitialFocusTarge = getInitialFocusTarget({
102
- container: el
121
+ container: el,
122
+ role: role
103
123
  })) === null || _getInitialFocusTarge === void 0 || _getInitialFocusTarge.focus();
104
124
  });
125
+ pendingRafRef.current = rafId;
105
126
  return function () {
106
127
  cancelAnimationFrame(rafId);
128
+ if (pendingRafRef.current === rafId) {
129
+ pendingRafRef.current = null;
130
+ }
107
131
  };
108
- // We intentionally depend on `role` even though `getInitialFocusTarget`
109
- // reads from the DOM attribute. This ensures the effect re-runs if
110
- // the role prop changes while the popover is open.
111
132
  }, [elementRef, isOpen, role]);
112
133
  }
@@ -5,16 +5,11 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.fromLegacyPlacement = fromLegacyPlacement;
8
- exports.placementMapping = void 0;
9
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
+ var _legacyPlacements = require("./legacy-placements");
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
- /**
14
- * Legacy Popper.js placement values (used by `@atlaskit/popper`, `@atlaskit/popup`,
15
- * `@atlaskit/tooltip`, etc.).
16
- */
17
-
18
13
  /**
19
14
  * Maps legacy Popper.js placement values to the new object-based
20
15
  * `Placement` values used by `@atlaskit/top-layer/popover`.
@@ -25,9 +20,11 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
25
20
  * | ------------------- | --------------------------------------------------- |
26
21
  * | `top` | `{ axis: 'block', edge: 'start', align: 'center' }` |
27
22
  * | `top-start` | `{ axis: 'block', edge: 'start', align: 'start' }` |
23
+ * | `top-center` | `{ axis: 'block', edge: 'start', align: 'center' }` |
28
24
  * | `top-end` | `{ axis: 'block', edge: 'start', align: 'end' }` |
29
25
  * | `bottom` | `{ axis: 'block', edge: 'end', align: 'center' }` |
30
26
  * | `bottom-start` | `{ axis: 'block', edge: 'end', align: 'start' }` |
27
+ * | `bottom-center` | `{ axis: 'block', edge: 'end', align: 'center' }` |
31
28
  * | `bottom-end` | `{ axis: 'block', edge: 'end', align: 'end' }` |
32
29
  * | `right` | `{ axis: 'inline', edge: 'end', align: 'center' }` |
33
30
  * | `right-start` | `{ axis: 'inline', edge: 'end', align: 'start' }` |
@@ -39,7 +36,10 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
39
36
  * | `auto-start` | `{ axis: 'block', edge: 'end', align: 'start' }` |
40
37
  * | `auto-end` | `{ axis: 'block', edge: 'end', align: 'end' }` |
41
38
  *
42
- * The `position-try-fallbacks` CSS property handles flipping automatically.
39
+ * `auto*` is mapped to `block-end` (a reduction in expressiveness vs Popper's
40
+ * runtime auto-flip). CSS `position-try-fallbacks` provides flipping at the
41
+ * edge - but the initial placement is fixed. If migrating away from Popper's
42
+ * dynamic auto, prefer an explicit placement.
43
43
  *
44
44
  * @example
45
45
  * ```ts
@@ -51,107 +51,10 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
51
51
  * // newPlacement === { axis: 'block', edge: 'end', align: 'start' }
52
52
  * ```
53
53
  */
54
- var placementMapping = exports.placementMapping = {
55
- top: {
56
- axis: 'block',
57
- edge: 'start',
58
- align: 'center'
59
- },
60
- 'top-start': {
61
- axis: 'block',
62
- edge: 'start',
63
- align: 'start'
64
- },
65
- 'top-center': {
66
- axis: 'block',
67
- edge: 'start',
68
- align: 'center'
69
- },
70
- 'top-end': {
71
- axis: 'block',
72
- edge: 'start',
73
- align: 'end'
74
- },
75
- bottom: {
76
- axis: 'block',
77
- edge: 'end',
78
- align: 'center'
79
- },
80
- 'bottom-start': {
81
- axis: 'block',
82
- edge: 'end',
83
- align: 'start'
84
- },
85
- 'bottom-center': {
86
- axis: 'block',
87
- edge: 'end',
88
- align: 'center'
89
- },
90
- 'bottom-end': {
91
- axis: 'block',
92
- edge: 'end',
93
- align: 'end'
94
- },
95
- right: {
96
- axis: 'inline',
97
- edge: 'end',
98
- align: 'center'
99
- },
100
- 'right-start': {
101
- axis: 'inline',
102
- edge: 'end',
103
- align: 'start'
104
- },
105
- 'right-end': {
106
- axis: 'inline',
107
- edge: 'end',
108
- align: 'end'
109
- },
110
- left: {
111
- axis: 'inline',
112
- edge: 'start',
113
- align: 'center'
114
- },
115
- 'left-start': {
116
- axis: 'inline',
117
- edge: 'start',
118
- align: 'start'
119
- },
120
- 'left-end': {
121
- axis: 'inline',
122
- edge: 'start',
123
- align: 'end'
124
- },
125
- auto: {
126
- axis: 'block',
127
- edge: 'end',
128
- align: 'center'
129
- },
130
- 'auto-start': {
131
- axis: 'block',
132
- edge: 'end',
133
- align: 'start'
134
- },
135
- 'auto-end': {
136
- axis: 'block',
137
- edge: 'end',
138
- align: 'end'
139
- }
140
- };
141
-
142
- /**
143
- * Convert a legacy Popper.js placement to the new `position-area`-based Placement.
144
- * Optional `offset` is the legacy Popper `[along, away]` tuple (also known as
145
- * `[skidding, distance]`); negative `along` becomes `direction: 'backwards'`.
146
- *
147
- * The tuple keeps the historical popper names so call sites that already pass
148
- * `[along, away]` migrate without thinking; the new internal field names
149
- * (`gap` / `crossAxisShift`) are produced inside this adapter.
150
- */
151
54
  function fromLegacyPlacement(_ref) {
152
55
  var legacy = _ref.legacy,
153
56
  offset = _ref.offset;
154
- var basePlacement = placementMapping[legacy];
57
+ var basePlacement = _legacyPlacements.placementMapping[legacy];
155
58
  if (!offset) {
156
59
  return basePlacement;
157
60
  }
@@ -168,8 +71,4 @@ function fromLegacyPlacement(_ref) {
168
71
  }
169
72
  }
170
73
  });
171
- }
172
-
173
- /**
174
- * The full mapping record, exposed for consumers that need to iterate or introspect.
175
- */
74
+ }
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.placementMapping = exports.LEGACY_PLACEMENTS = void 0;
7
+ /**
8
+ * Legacy Popper.js placement values (used by `@atlaskit/popper`,
9
+ * `@atlaskit/popup`, `@atlaskit/tooltip`, etc.). Re-exported from
10
+ * `@atlaskit/top-layer/placement-map` for migration consumers that need
11
+ * to enumerate every placement (e.g. for a settings dropdown). New code
12
+ * should use the object-based `TPlacementOptions` directly.
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
+ };
@@ -15,8 +15,8 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
15
15
  var _react = _interopRequireWildcard(require("react"));
16
16
  var _bindEventListener = require("bind-event-listener");
17
17
  var _mergeRefs = _interopRequireDefault(require("@atlaskit/ds-lib/merge-refs"));
18
- var _noop = _interopRequireDefault(require("@atlaskit/ds-lib/noop"));
19
18
  var _once = _interopRequireDefault(require("@atlaskit/ds-lib/once"));
19
+ var _openLayerObserver = require("@atlaskit/layering/experimental/open-layer-observer");
20
20
  var _useAnimatedVisibility = require("../internal/use-animated-visibility");
21
21
  var _useFocusWrap = require("../internal/use-focus-wrap");
22
22
  var _useInitialFocus = require("../internal/use-initial-focus");
@@ -40,6 +40,18 @@ var supportsPopoverHint = (0, _once.default)(function () {
40
40
  var styles = {
41
41
  root: "_19itglyw _ca0qidpf _u5f3idpf _n3tdidpf _19bvidpf _19pkidpf _2hwxidpf _otyridpf _18u0idpf _1r041wug _1reoewfl _18m9ewfl _bfhk1j28"
42
42
  };
43
+ /**
44
+ * ARIA roles on the Popover primitive that register with the open layer observer
45
+ * as LayerType 'popup'. These are interactive overlays that should be tracked
46
+ * and dismissed when closeLayers() is called (e.g. when the side nav resizes).
47
+ *
48
+ * Note on open layer observer types not included here:
49
+ * - 'modal' only ever comes from the Dialog primitive, not Popover.
50
+ * - Other layer types (e.g. 'tooltip') do not yet exist in LayerType because
51
+ * there is no current use case for distinguishing them in the observer.
52
+ */
53
+ var POPUP_ROLES = new Set(['menu', 'listbox', 'dialog', 'alertdialog', 'tree', 'grid']);
54
+
43
55
  /**
44
56
  * Unopinionated top-layer primitive.
45
57
  *
@@ -54,15 +66,14 @@ var styles = {
54
66
  *
55
67
  * For `mode="auto"`, the browser can dismiss the popover via light dismiss
56
68
  * (Escape, click outside). When that happens, `onClose` is called and the
57
- * consumer must set `isOpen` to `false`. The DOM owns the dismiss — React
69
+ * consumer must set `isOpen` to `false`. The DOM owns the dismiss; React
58
70
  * must follow.
59
71
  */
60
72
  var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Popover(_ref, ref) {
61
73
  var children = _ref.children,
62
74
  _ref$mode = _ref.mode,
63
75
  modeProp = _ref$mode === void 0 ? 'auto' : _ref$mode,
64
- _ref$onClose = _ref.onClose,
65
- onClose = _ref$onClose === void 0 ? _noop.default : _ref$onClose,
76
+ onClose = _ref.onClose,
66
77
  onOpenChange = _ref.onOpenChange,
67
78
  onExitFinish = _ref.onExitFinish,
68
79
  animate = _ref.animate,
@@ -77,7 +88,7 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
77
88
  var autoId = (0, _react.useId)();
78
89
  var ownRef = (0, _react.useRef)(null);
79
90
  var combinedRef = (0, _mergeRefs.default)([ownRef, ref].filter(Boolean));
80
- // Regex is intentional — `replaceAll` is not available in our supported
91
+ // Regex is intentional: `replaceAll` is not available in our supported
81
92
  // browser matrix. In React 18, `useId()` returns IDs containing colons
82
93
  // (e.g. `:r1:`) which are invalid in CSS selectors and `popover` target
83
94
  // attributes. React 19.2.0 replaced colons with underscores, making this
@@ -95,7 +106,7 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
95
106
  // ── Focus management ──
96
107
  // Initial focus: on open, moves focus into the popover (role-dependent).
97
108
  // Focus wrap: Tab/Shift+Tab cycle within the popover for dialog roles.
98
- // Focus restoration: handled natively by the Popover API — the browser
109
+ // Focus restoration: handled natively by the Popover API. The browser
99
110
  // tracks `previouslyFocusedElement` on show and restores it on hide.
100
111
  // No custom restoration hook is needed. See: notes/architecture/focus-restoration.md
101
112
  (0, _useFocusWrap.useFocusWrap)({
@@ -108,6 +119,24 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
108
119
  role: role
109
120
  });
110
121
 
122
+ // ── Open layer observer registration ──
123
+ // Notifies the open layer observer so app-coordination features
124
+ // (closeLayers(), open-count subscriptions) work with top-layer popovers.
125
+ //
126
+ // Only set type: 'popup' for roles that are interactive overlay popups.
127
+ // Passive/informational roles (tooltip, note, status, alert, log) and
128
+ // no-role cases do not map to LayerType 'popup', so type is left undefined.
129
+ var handleObserverClose = (0, _react.useCallback)(function () {
130
+ onClose === null || onClose === void 0 || onClose({
131
+ reason: 'programmatic'
132
+ });
133
+ }, [onClose]);
134
+ (0, _openLayerObserver.useNotifyOpenLayerObserver)({
135
+ type: role && POPUP_ROLES.has(role) ? 'popup' : undefined,
136
+ isOpen: isOpen,
137
+ onClose: handleObserverClose
138
+ });
139
+
111
140
  // Hint mode fallback: "hint" downgrades to "auto" if unsupported.
112
141
  //
113
142
  // Decision (2026-03-17 audit): We keep "auto" as the fallback (not "manual")
@@ -120,8 +149,8 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
120
149
  var mode = modeProp === 'hint' && !supportsPopoverHint() ? 'auto' : modeProp;
121
150
 
122
151
  // Distinguishes programmatic hidePopover() from browser-initiated dismiss.
123
- // When we call hidePopover() ourselves the beforetoggle event fires synchronously —
124
- // this ref prevents the toggle handler from calling onClose redundantly.
152
+ // When we call hidePopover() ourselves the beforetoggle event fires synchronously,
153
+ // so this ref prevents the toggle handler from calling onClose redundantly.
125
154
  var programmaticCloseRef = (0, _react.useRef)(false);
126
155
 
127
156
  // Escape key tracking: set in a capture-phase keydown listener (fires before
@@ -154,7 +183,11 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
154
183
  // Browser dismiss (Escape or click-outside). Notify consumer.
155
184
  var reason = closeReasonRef.current;
156
185
  closeReasonRef.current = 'light-dismiss'; // reset for next dismiss
157
- onClose({
186
+ // `onClose` is required for `auto`/`hint` and forbidden for
187
+ // `manual`, but `TPopoverForwardedProps` is a flat type used
188
+ // internally by `PopupContent` - the optional check guards
189
+ // the manual-mode forwarded path where the field is undefined.
190
+ onClose === null || onClose === void 0 || onClose({
158
191
  reason: reason
159
192
  });
160
193
  }
@@ -189,7 +222,7 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
189
222
 
190
223
  // Placement-dependent animation CSS vars (e.g. slide direction).
191
224
  // Separate from show/hide so that preset reference changes
192
- // (e.g. inline `animate={slideAndFade()}`) don't re-trigger visibility.
225
+ // (e.g. inline `animate={slideAndFade()}`) do not re-trigger visibility.
193
226
  (0, _react.useLayoutEffect)(function () {
194
227
  var el = ownRef.current;
195
228
  if (!el || !(preset !== null && preset !== void 0 && preset.getProperties) || !placement) {
@@ -211,7 +244,7 @@ var Popover = exports.Popover = /*#__PURE__*/(0, _react.forwardRef)(function Pop
211
244
  // in the target state (e.g. browser dismissed via light-dismiss before our
212
245
  // effect ran).
213
246
  // Defensive try/catch: these APIs throw InvalidStateError if the element
214
- // is disconnected or missing the popover attribute. While we don't expect
247
+ // is disconnected or missing the popover attribute. While we do not expect
215
248
  // that to happen in normal usage, it can occur in edge cases (e.g. React
216
249
  // concurrent mode, StrictMode double-effects, or unmount races).
217
250
  (0, _react.useLayoutEffect)(function () {
@@ -40,7 +40,8 @@ var PopupContent = exports.PopupContent = /*#__PURE__*/(0, _react.forwardRef)(fu
40
40
  placementProp = _ref.placement,
41
41
  onCloseProp = _ref.onClose,
42
42
  testIdProp = _ref.testId,
43
- forceFallbackPositioningProp = _ref.forceFallbackPositioning;
43
+ forceFallbackPositioningProp = _ref.forceFallbackPositioning,
44
+ idProp = _ref.id;
44
45
  // When inside <Popup>, context provides defaults. Props always win.
45
46
  var ctx = (0, _popupContext.useMaybePopupContext)();
46
47
  var triggerRef = triggerRefProp !== null && triggerRefProp !== void 0 ? triggerRefProp : ctx === null || ctx === void 0 ? void 0 : ctx.triggerRef;
@@ -83,7 +84,7 @@ var PopupContent = exports.PopupContent = /*#__PURE__*/(0, _react.forwardRef)(fu
83
84
  // does not restore focus when they close.
84
85
  //
85
86
  // To handle this, we listen for the close transition via `onOpenChange` and
86
- // restore focus to the trigger if the browser didn't. This runs after the
87
+ // restore focus to the trigger if the browser did not. This runs after the
87
88
  // browser's native restoration (which happens synchronously during hidePopover),
88
89
  // so it only takes effect when the browser skipped restoration (nested case).
89
90
  //
@@ -100,7 +101,7 @@ var PopupContent = exports.PopupContent = /*#__PURE__*/(0, _react.forwardRef)(fu
100
101
  // focus when they close.
101
102
  //
102
103
  // We detect this case by checking if focus is still inside the
103
- // closing popover element. If it is, the browser didn't restore,
104
+ // closing popover element. If it is, the browser did not restore,
104
105
  // and we need to move focus to the trigger ourselves.
105
106
  //
106
107
  // This does NOT fire for:
@@ -109,13 +110,15 @@ var PopupContent = exports.PopupContent = /*#__PURE__*/(0, _react.forwardRef)(fu
109
110
  if (!args.isOpen) {
110
111
  var trigger = triggerRef === null || triggerRef === void 0 ? void 0 : triggerRef.current;
111
112
  var focusStillInPopover = trigger && args.element.contains(document.activeElement);
112
- if (focusStillInPopover) {
113
- var shouldRestore = role === 'dialog' || role === 'alertdialog' || role === 'menu' || role === 'listbox';
114
- if (shouldRestore) {
115
- trigger.focus({
116
- preventScroll: true
117
- });
118
- }
113
+ // Single source of truth for "this role moves focus on open
114
+ // and therefore needs to be restored on close" - see
115
+ // `shouldFocusIntoPopover` in `internal/role-types.tsx`.
116
+ if (focusStillInPopover && (0, _roleTypes.shouldFocusIntoPopover)({
117
+ role: role
118
+ })) {
119
+ trigger.focus({
120
+ preventScroll: true
121
+ });
119
122
  }
120
123
  }
121
124
  }, [setIsOpen, consumerOnOpenChange, triggerRef, role]);
@@ -145,8 +148,8 @@ var PopupContent = exports.PopupContent = /*#__PURE__*/(0, _react.forwardRef)(fu
145
148
  var combinedRef = (0, _mergeRefs.default)([popoverRef, ref]);
146
149
 
147
150
  // TPopupContentProps already validates the discriminated union (role + label,
148
- // mode + onClose) at its API boundary. Popover accepts TPopoverInternalProps
149
- // internally so individual prop forwarding doesn't need to re-prove the union.
151
+ // mode + onClose) at its API boundary. Popover accepts TPopoverForwardedProps
152
+ // internally so individual prop forwarding does not need to re-prove the union.
150
153
  var popoverProps = {
151
154
  isOpen: isOpen,
152
155
  animate: animate,
@@ -159,7 +162,10 @@ var PopupContent = exports.PopupContent = /*#__PURE__*/(0, _react.forwardRef)(fu
159
162
  onOpenChange: handleOpenChange,
160
163
  testId: testId,
161
164
  children: children,
162
- id: ctx === null || ctx === void 0 ? void 0 : ctx.popoverId,
165
+ // Standalone consumers can pass an explicit `id` - used by their own
166
+ // trigger to wire `aria-controls`. Inside the `<Popup>` compound, the
167
+ // id flows from context (`ctx.popoverId`).
168
+ id: idProp !== null && idProp !== void 0 ? idProp : ctx === null || ctx === void 0 ? void 0 : ctx.popoverId,
163
169
  xcss: xcss
164
170
  };
165
171
  return /*#__PURE__*/_react.default.createElement(_popover.Popover, (0, _extends2.default)({
@@ -23,6 +23,7 @@ var PopupProvider = exports.PopupProvider = PopupContext.Provider;
23
23
  *
24
24
  * Throws if called outside of a `<Popup>` compound component.
25
25
  */
26
+ // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
26
27
  function usePopupContext() {
27
28
  var ctx = (0, _react.useContext)(PopupContext);
28
29
  if (ctx === null) {
@@ -37,6 +38,7 @@ function usePopupContext() {
37
38
  * Used by `PopupContent` to support standalone usage (e.g. tooltip)
38
39
  * where values are passed as props instead of coming from a compound component.
39
40
  */
41
+ // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
40
42
  function useMaybePopupContext() {
41
43
  return (0, _react.useContext)(PopupContext);
42
44
  }
@@ -21,6 +21,12 @@ var styles = {
21
21
  * should use `Popup.Content` without wrapping in `PopupSurface`.
22
22
  *
23
23
  * Exposed as `Popup.Surface` on the compound component.
24
+ *
25
+ * **Presentational primitive - `children`-only by design.** This component
26
+ * intentionally exposes no `style`, `className`, `xcss`, `id`, or `ref`
27
+ * props. If you need any of those, render your own wrapper element inside
28
+ * `Popup.Content` instead - `PopupSurface` is the no-config "give me the
29
+ * default ADS overlay look" surface, nothing more.
24
30
  */
25
31
  function PopupSurface(_ref) {
26
32
  var children = _ref.children;
@@ -13,7 +13,7 @@ var _popupContext = require("./popup-context");
13
13
  /**
14
14
  * Render-prop alternative to `Popup.Trigger`.
15
15
  *
16
- * Use when you need full control over the trigger element — for example,
16
+ * Use when you need full control over the trigger element. For example,
17
17
  * to compose your own trigger with custom event handlers, wrap a third-party
18
18
  * component, or build a trigger from multiple elements.
19
19
  *