@atlaskit/top-layer 0.12.2 → 0.14.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 (245) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +61 -34
  3. package/dist/cjs/animations/presets.js +4 -4
  4. package/dist/cjs/entry-points/focus.js +6 -4
  5. package/dist/cjs/entry-points/get-aria-for-trigger.js +12 -0
  6. package/dist/cjs/entry-points/popover-surface.js +12 -0
  7. package/dist/cjs/entry-points/use-anchor-position-at-point.js +12 -0
  8. package/dist/cjs/entry-points/use-popover-id.js +21 -0
  9. package/dist/cjs/focus/get-first-focusable.js +19 -0
  10. package/dist/cjs/focus/get-focusables.js +30 -0
  11. package/dist/cjs/focus/get-last-focusable.js +19 -0
  12. package/dist/cjs/focus/get-next-focusable.js +43 -0
  13. package/dist/cjs/focus/is-in-same-layer.js +23 -0
  14. package/dist/cjs/focus/is-nested-layer-focused.js +32 -0
  15. package/dist/cjs/focus/selectors.js +19 -0
  16. package/dist/cjs/focus/types.js +1 -0
  17. package/dist/cjs/internal/get-aria-for-trigger.js +88 -0
  18. package/dist/cjs/internal/role-to-aria-has-popup.js +37 -0
  19. package/dist/cjs/internal/role-types.js +2 -35
  20. package/dist/cjs/internal/should-focus-into-popover.js +19 -0
  21. package/dist/cjs/internal/slot.js +1 -1
  22. package/dist/cjs/internal/use-anchor-position-at-point.js +127 -0
  23. package/dist/cjs/internal/use-anchor-position.js +36 -16
  24. package/dist/cjs/internal/use-focus-wrap.js +8 -5
  25. package/dist/cjs/internal/use-initial-focus.js +4 -4
  26. package/dist/cjs/placement-map/index.js +2 -2
  27. package/dist/cjs/placement-map/legacy-placements.js +2 -101
  28. package/dist/cjs/placement-map/placement-mapping.js +103 -0
  29. package/dist/cjs/popover/popover.js +79 -80
  30. package/dist/cjs/popover-surface/index.js +12 -0
  31. package/dist/cjs/{popup/popup-surface.js → popover-surface/popover-surface.js} +9 -12
  32. package/dist/cjs/testing/polyfill.js +9 -6
  33. package/dist/cjs/use-arrow-navigation/use-arrow-navigation.js +7 -5
  34. package/dist/es2019/animations/presets.js +4 -4
  35. package/dist/es2019/entry-points/focus.js +3 -1
  36. package/dist/es2019/entry-points/get-aria-for-trigger.js +1 -0
  37. package/dist/es2019/entry-points/popover-surface.js +1 -0
  38. package/dist/es2019/entry-points/use-anchor-position-at-point.js +1 -0
  39. package/dist/es2019/entry-points/use-popover-id.js +15 -0
  40. package/dist/es2019/focus/get-first-focusable.js +14 -0
  41. package/dist/es2019/focus/get-focusables.js +19 -0
  42. package/dist/es2019/focus/get-last-focusable.js +14 -0
  43. package/dist/es2019/focus/get-next-focusable.js +38 -0
  44. package/dist/es2019/focus/is-in-same-layer.js +18 -0
  45. package/dist/es2019/focus/is-nested-layer-focused.js +28 -0
  46. package/dist/es2019/focus/selectors.js +11 -0
  47. package/dist/es2019/focus/types.js +0 -0
  48. package/dist/es2019/internal/get-aria-for-trigger.js +84 -0
  49. package/dist/es2019/internal/role-to-aria-has-popup.js +32 -0
  50. package/dist/es2019/internal/role-types.js +0 -33
  51. package/dist/es2019/internal/should-focus-into-popover.js +14 -0
  52. package/dist/es2019/internal/slot.js +1 -1
  53. package/dist/es2019/internal/use-anchor-position-at-point.js +122 -0
  54. package/dist/es2019/internal/use-anchor-position.js +21 -14
  55. package/dist/es2019/internal/use-focus-wrap.js +4 -1
  56. package/dist/es2019/internal/use-initial-focus.js +1 -1
  57. package/dist/es2019/placement-map/index.js +1 -1
  58. package/dist/es2019/placement-map/legacy-placements.js +1 -100
  59. package/dist/es2019/placement-map/placement-mapping.js +97 -0
  60. package/dist/es2019/popover/popover.js +79 -80
  61. package/dist/es2019/popover-surface/index.js +1 -0
  62. package/dist/es2019/popover-surface/popover-surface.js +27 -0
  63. package/dist/es2019/testing/polyfill.js +9 -6
  64. package/dist/es2019/use-arrow-navigation/use-arrow-navigation.js +3 -1
  65. package/dist/esm/animations/presets.js +4 -4
  66. package/dist/esm/entry-points/focus.js +3 -1
  67. package/dist/esm/entry-points/get-aria-for-trigger.js +1 -0
  68. package/dist/esm/entry-points/popover-surface.js +1 -0
  69. package/dist/esm/entry-points/use-anchor-position-at-point.js +1 -0
  70. package/dist/esm/entry-points/use-popover-id.js +15 -0
  71. package/dist/esm/focus/get-first-focusable.js +13 -0
  72. package/dist/esm/focus/get-focusables.js +24 -0
  73. package/dist/esm/focus/get-last-focusable.js +13 -0
  74. package/dist/esm/focus/get-next-focusable.js +37 -0
  75. package/dist/esm/focus/is-in-same-layer.js +17 -0
  76. package/dist/esm/focus/is-nested-layer-focused.js +27 -0
  77. package/dist/esm/focus/selectors.js +13 -0
  78. package/dist/esm/focus/types.js +0 -0
  79. package/dist/esm/internal/get-aria-for-trigger.js +83 -0
  80. package/dist/esm/internal/role-to-aria-has-popup.js +31 -0
  81. package/dist/esm/internal/role-types.js +2 -34
  82. package/dist/esm/internal/should-focus-into-popover.js +13 -0
  83. package/dist/esm/internal/slot.js +1 -1
  84. package/dist/esm/internal/use-anchor-position-at-point.js +122 -0
  85. package/dist/esm/internal/use-anchor-position.js +36 -16
  86. package/dist/esm/internal/use-focus-wrap.js +4 -1
  87. package/dist/esm/internal/use-initial-focus.js +1 -1
  88. package/dist/esm/placement-map/index.js +1 -1
  89. package/dist/esm/placement-map/legacy-placements.js +1 -100
  90. package/dist/esm/placement-map/placement-mapping.js +97 -0
  91. package/dist/esm/popover/popover.js +79 -80
  92. package/dist/esm/popover-surface/index.js +1 -0
  93. package/dist/esm/popover-surface/popover-surface.js +26 -0
  94. package/dist/esm/testing/polyfill.js +9 -6
  95. package/dist/esm/use-arrow-navigation/use-arrow-navigation.js +3 -1
  96. package/dist/types/animations/presets.d.ts +4 -4
  97. package/dist/types/animations/types.d.ts +1 -1
  98. package/dist/types/dialog/types.d.ts +0 -8
  99. package/dist/types/entry-points/focus.d.ts +4 -2
  100. package/dist/types/entry-points/get-aria-for-trigger.d.ts +1 -0
  101. package/dist/types/entry-points/popover-surface.d.ts +2 -0
  102. package/dist/types/entry-points/popover.d.ts +1 -1
  103. package/dist/types/entry-points/use-anchor-position-at-point.d.ts +1 -0
  104. package/dist/types/entry-points/use-popover-id.d.ts +11 -0
  105. package/dist/types/focus/get-first-focusable.d.ts +8 -0
  106. package/dist/types/focus/get-focusables.d.ts +13 -0
  107. package/dist/types/focus/get-last-focusable.d.ts +8 -0
  108. package/dist/types/focus/get-next-focusable.d.ts +13 -0
  109. package/dist/types/focus/is-in-same-layer.d.ts +8 -0
  110. package/dist/types/focus/is-nested-layer-focused.d.ts +8 -0
  111. package/dist/types/focus/selectors.d.ts +4 -0
  112. package/dist/types/focus/types.d.ts +5 -0
  113. package/dist/types/internal/get-aria-for-trigger.d.ts +86 -0
  114. package/dist/types/internal/role-to-aria-has-popup.d.ts +13 -0
  115. package/dist/types/internal/role-types.d.ts +0 -12
  116. package/dist/types/internal/should-focus-into-popover.d.ts +8 -0
  117. package/dist/types/internal/slot.d.ts +2 -2
  118. package/dist/types/internal/use-anchor-position-at-point.d.ts +62 -0
  119. package/dist/types/internal/use-anchor-position.d.ts +6 -1
  120. package/dist/types/placement-map/index.d.ts +1 -1
  121. package/dist/types/placement-map/legacy-placements.d.ts +0 -12
  122. package/dist/types/placement-map/placement-mapping.d.ts +13 -0
  123. package/dist/types/popover/index.d.ts +1 -1
  124. package/dist/types/popover/popover.d.ts +6 -13
  125. package/dist/types/popover/types.d.ts +6 -5
  126. package/dist/types/popover-surface/index.d.ts +2 -0
  127. package/dist/types/popover-surface/popover-surface.d.ts +21 -0
  128. package/dist/types/use-arrow-navigation/use-arrow-navigation.d.ts +1 -1
  129. package/dist/types-ts4.5/animations/presets.d.ts +4 -4
  130. package/dist/types-ts4.5/animations/types.d.ts +1 -1
  131. package/dist/types-ts4.5/dialog/types.d.ts +0 -8
  132. package/dist/types-ts4.5/entry-points/focus.d.ts +4 -2
  133. package/dist/types-ts4.5/entry-points/get-aria-for-trigger.d.ts +1 -0
  134. package/dist/types-ts4.5/entry-points/popover-surface.d.ts +2 -0
  135. package/dist/types-ts4.5/entry-points/popover.d.ts +1 -1
  136. package/dist/types-ts4.5/entry-points/use-anchor-position-at-point.d.ts +1 -0
  137. package/dist/types-ts4.5/entry-points/use-popover-id.d.ts +11 -0
  138. package/dist/types-ts4.5/focus/get-first-focusable.d.ts +8 -0
  139. package/dist/types-ts4.5/focus/get-focusables.d.ts +13 -0
  140. package/dist/types-ts4.5/focus/get-last-focusable.d.ts +8 -0
  141. package/dist/types-ts4.5/focus/get-next-focusable.d.ts +13 -0
  142. package/dist/types-ts4.5/focus/is-in-same-layer.d.ts +8 -0
  143. package/dist/types-ts4.5/focus/is-nested-layer-focused.d.ts +8 -0
  144. package/dist/types-ts4.5/focus/selectors.d.ts +4 -0
  145. package/dist/types-ts4.5/focus/types.d.ts +5 -0
  146. package/dist/types-ts4.5/internal/get-aria-for-trigger.d.ts +86 -0
  147. package/dist/types-ts4.5/internal/role-to-aria-has-popup.d.ts +13 -0
  148. package/dist/types-ts4.5/internal/role-types.d.ts +0 -12
  149. package/dist/types-ts4.5/internal/should-focus-into-popover.d.ts +8 -0
  150. package/dist/types-ts4.5/internal/slot.d.ts +2 -2
  151. package/dist/types-ts4.5/internal/use-anchor-position-at-point.d.ts +62 -0
  152. package/dist/types-ts4.5/internal/use-anchor-position.d.ts +6 -1
  153. package/dist/types-ts4.5/placement-map/index.d.ts +1 -1
  154. package/dist/types-ts4.5/placement-map/legacy-placements.d.ts +0 -12
  155. package/dist/types-ts4.5/placement-map/placement-mapping.d.ts +13 -0
  156. package/dist/types-ts4.5/popover/index.d.ts +1 -1
  157. package/dist/types-ts4.5/popover/popover.d.ts +6 -13
  158. package/dist/types-ts4.5/popover/types.d.ts +6 -5
  159. package/dist/types-ts4.5/popover-surface/index.d.ts +2 -0
  160. package/dist/types-ts4.5/popover-surface/popover-surface.d.ts +21 -0
  161. package/dist/types-ts4.5/use-arrow-navigation/use-arrow-navigation.d.ts +1 -1
  162. package/get-aria-for-trigger/package.json +17 -0
  163. package/package.json +3 -3
  164. package/popover-surface/package.json +17 -0
  165. package/use-anchor-position-at-point/package.json +17 -0
  166. package/use-popover-id/package.json +17 -0
  167. package/dist/cjs/entry-points/popup-surface.js +0 -12
  168. package/dist/cjs/entry-points/popup.js +0 -12
  169. package/dist/cjs/focus/focus.js +0 -169
  170. package/dist/cjs/focus/index.js +0 -24
  171. package/dist/cjs/popup/index.js +0 -37
  172. package/dist/cjs/popup/popup-content.js +0 -214
  173. package/dist/cjs/popup/popup-context.js +0 -25
  174. package/dist/cjs/popup/popup-provider.js +0 -15
  175. package/dist/cjs/popup/popup-trigger-function.js +0 -80
  176. package/dist/cjs/popup/popup-trigger.js +0 -66
  177. package/dist/cjs/popup/popup.js +0 -81
  178. package/dist/cjs/popup/types.js +0 -5
  179. package/dist/cjs/popup/use-maybe-popup-context.js +0 -17
  180. package/dist/cjs/popup/use-popup-context.js +0 -20
  181. package/dist/es2019/entry-points/popup-surface.js +0 -1
  182. package/dist/es2019/entry-points/popup.js +0 -1
  183. package/dist/es2019/focus/focus.js +0 -158
  184. package/dist/es2019/focus/index.js +0 -1
  185. package/dist/es2019/popup/index.js +0 -33
  186. package/dist/es2019/popup/popup-content.js +0 -206
  187. package/dist/es2019/popup/popup-context.js +0 -20
  188. package/dist/es2019/popup/popup-provider.js +0 -10
  189. package/dist/es2019/popup/popup-surface.js +0 -30
  190. package/dist/es2019/popup/popup-trigger-function.js +0 -75
  191. package/dist/es2019/popup/popup-trigger.js +0 -61
  192. package/dist/es2019/popup/popup.js +0 -67
  193. package/dist/es2019/popup/types.js +0 -1
  194. package/dist/es2019/popup/use-maybe-popup-context.js +0 -12
  195. package/dist/es2019/popup/use-popup-context.js +0 -15
  196. package/dist/esm/entry-points/popup-surface.js +0 -1
  197. package/dist/esm/entry-points/popup.js +0 -1
  198. package/dist/esm/focus/focus.js +0 -160
  199. package/dist/esm/focus/index.js +0 -1
  200. package/dist/esm/popup/index.js +0 -33
  201. package/dist/esm/popup/popup-content.js +0 -206
  202. package/dist/esm/popup/popup-context.js +0 -20
  203. package/dist/esm/popup/popup-provider.js +0 -10
  204. package/dist/esm/popup/popup-surface.js +0 -29
  205. package/dist/esm/popup/popup-trigger-function.js +0 -75
  206. package/dist/esm/popup/popup-trigger.js +0 -59
  207. package/dist/esm/popup/popup.js +0 -72
  208. package/dist/esm/popup/types.js +0 -1
  209. package/dist/esm/popup/use-maybe-popup-context.js +0 -12
  210. package/dist/esm/popup/use-popup-context.js +0 -15
  211. package/dist/types/entry-points/popup-surface.d.ts +0 -2
  212. package/dist/types/entry-points/popup.d.ts +0 -2
  213. package/dist/types/focus/focus.d.ts +0 -50
  214. package/dist/types/focus/index.d.ts +0 -2
  215. package/dist/types/popup/index.d.ts +0 -34
  216. package/dist/types/popup/popup-content.d.ts +0 -14
  217. package/dist/types/popup/popup-context.d.ts +0 -96
  218. package/dist/types/popup/popup-provider.d.ts +0 -10
  219. package/dist/types/popup/popup-surface.d.ts +0 -22
  220. package/dist/types/popup/popup-trigger-function.d.ts +0 -112
  221. package/dist/types/popup/popup-trigger.d.ts +0 -14
  222. package/dist/types/popup/popup.d.ts +0 -14
  223. package/dist/types/popup/types.d.ts +0 -321
  224. package/dist/types/popup/use-maybe-popup-context.d.ts +0 -8
  225. package/dist/types/popup/use-popup-context.d.ts +0 -7
  226. package/dist/types-ts4.5/entry-points/popup-surface.d.ts +0 -2
  227. package/dist/types-ts4.5/entry-points/popup.d.ts +0 -2
  228. package/dist/types-ts4.5/focus/focus.d.ts +0 -50
  229. package/dist/types-ts4.5/focus/index.d.ts +0 -2
  230. package/dist/types-ts4.5/popup/index.d.ts +0 -34
  231. package/dist/types-ts4.5/popup/popup-content.d.ts +0 -14
  232. package/dist/types-ts4.5/popup/popup-context.d.ts +0 -96
  233. package/dist/types-ts4.5/popup/popup-provider.d.ts +0 -10
  234. package/dist/types-ts4.5/popup/popup-surface.d.ts +0 -22
  235. package/dist/types-ts4.5/popup/popup-trigger-function.d.ts +0 -112
  236. package/dist/types-ts4.5/popup/popup-trigger.d.ts +0 -14
  237. package/dist/types-ts4.5/popup/popup.d.ts +0 -14
  238. package/dist/types-ts4.5/popup/types.d.ts +0 -321
  239. package/dist/types-ts4.5/popup/use-maybe-popup-context.d.ts +0 -8
  240. package/dist/types-ts4.5/popup/use-popup-context.d.ts +0 -7
  241. package/popup/package.json +0 -17
  242. package/popup-surface/package.json +0 -17
  243. /package/dist/cjs/{popup/popup-surface.compiled.css → popover-surface/popover-surface.compiled.css} +0 -0
  244. /package/dist/es2019/{popup/popup-surface.compiled.css → popover-surface/popover-surface.compiled.css} +0 -0
  245. /package/dist/esm/{popup/popup-surface.compiled.css → popover-surface/popover-surface.compiled.css} +0 -0
@@ -1,158 +0,0 @@
1
- /**
2
- * Focusable element selectors and navigation helpers.
3
- *
4
- * Uses CSS selectors to discover focusable elements within a container,
5
- * avoiding the need for a manual registration system. Disabled elements
6
- * (both HTML `disabled` attribute and `aria-disabled="true"`) and elements
7
- * with `tabindex="-1"` are excluded automatically.
8
- *
9
- * All public functions accept an optional `filter` callback to further
10
- * restrict which focusable elements are included.
11
- */
12
-
13
- import { getDocument } from '@atlaskit/browser-apis';
14
- const selectors = (() => {
15
- // Common exclusion filter applied to every selector.
16
- const not = ':not([tabindex="-1"]):not([aria-disabled="true"]):not([aria-hidden="true"])';
17
- const notDisabled = `:not([disabled])${not}`;
18
- const focusable = [`a[href]${not}`, `area[href]${not}`, `input${notDisabled}:not([type="hidden"])`, `select${notDisabled}`, `textarea${notDisabled}`, `button${notDisabled}`, `iframe${not}`, `audio[controls]${not}`, `video[controls]${not}`, `[contenteditable]:not([contenteditable="false"])${not}`, `[tabindex]${notDisabled}${not}`];
19
- return {
20
- focusable: focusable.join(','),
21
- // Build the `:focus` selector
22
- focused: focusable.map(selector => `${selector}:focus`).join(',')
23
- };
24
- })();
25
- const topLayerSelector = '[popover], dialog, [role="dialog"], [role="alertdialog"]';
26
-
27
- /**
28
- * Returns true when `element` and `container` resolve to the same nearest
29
- * top-layer host (or both resolve to `null`, meaning the document layer).
30
- */
31
- function isInSameLayer({
32
- element,
33
- container
34
- }) {
35
- const elementLayer = element.closest(topLayerSelector);
36
- const containerLayer = container.closest(topLayerSelector);
37
- // Both elements are in the "document" layer
38
- if (!elementLayer && !containerLayer) {
39
- return true;
40
- }
41
- return elementLayer === containerLayer;
42
- }
43
-
44
- /**
45
- * Returns `true` when `document.activeElement` is inside a nested top-layer
46
- * descendant of `container` (a `[popover]`, `<dialog>`, `[role="dialog"]` or
47
- * `[role="alertdialog"]` that is itself a descendant of `container`).
48
- */
49
- export function isNestedLayerFocused({
50
- container
51
- }) {
52
- var _getDocument;
53
- const focused = (_getDocument = getDocument()) === null || _getDocument === void 0 ? void 0 : _getDocument.activeElement;
54
- if (!(focused instanceof HTMLElement)) {
55
- return false;
56
- }
57
-
58
- // Cannot be in a nested layer if it is not in the container
59
- if (!container.contains(focused)) {
60
- return false;
61
- }
62
-
63
- // If not in the same layer, must be in a nested layer
64
- return !isInSameLayer({
65
- element: focused,
66
- container
67
- });
68
- }
69
-
70
- /**
71
- * An optional callback to restrict which focusable elements are included.
72
- * Return `true` to include the element, `false` to skip it.
73
- */
74
-
75
- /**
76
- * Returns all focusable HTMLElements within the container, optionally filtered.
77
- *
78
- * Focusables that belong to a nested top-layer scope (a `[popover]` or
79
- * `<dialog>` descendant of the container) are excluded - those elements
80
- * are owned by the inner layer's focus management. The container itself
81
- * is allowed to be a popover/dialog; only nested ones are filtered.
82
- */
83
- function getFocusables({
84
- container,
85
- filter
86
- }) {
87
- return Array.from(container.querySelectorAll(selectors.focusable)).filter(element => element instanceof HTMLElement).filter(element => isInSameLayer({
88
- element,
89
- container
90
- })).filter(element => !filter || filter(element, container));
91
- }
92
-
93
- /**
94
- * Returns the first focusable element within the container.
95
- */
96
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
97
- export function getFirstFocusable({
98
- container,
99
- filter
100
- }) {
101
- const items = getFocusables({
102
- container,
103
- filter
104
- });
105
- return items.length > 0 ? items[0] : null;
106
- }
107
-
108
- /**
109
- * Returns the last focusable element within the container.
110
- */
111
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
112
- export function getLastFocusable({
113
- container,
114
- filter
115
- }) {
116
- const items = getFocusables({
117
- container,
118
- filter
119
- });
120
- return items.length > 0 ? items[items.length - 1] : null;
121
- }
122
-
123
- /**
124
- * Returns the next focusable element in the given direction relative to the
125
- * currently focused element within the container. Wraps around at both ends.
126
- *
127
- * Returns `null` if there is no focused element within the container or if
128
- * the focused element is not in the list of focusable elements.
129
- */
130
- // eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
131
- export function getNextFocusable({
132
- container,
133
- direction,
134
- filter
135
- }) {
136
- var _all$nextIndex;
137
- const all = getFocusables({
138
- container,
139
- filter
140
- });
141
- const current = container.querySelector(selectors.focused);
142
- if (!(current instanceof HTMLElement)) {
143
- return null;
144
- }
145
- const index = all.indexOf(current);
146
- if (index === -1) {
147
- return null;
148
- }
149
- const nextIndex = (() => {
150
- if (direction === 'forwards') {
151
- return (index + 1) % all.length;
152
- }
153
- const proposed = index - 1;
154
- // if going into negative numbers, loop back to the last item
155
- return proposed < 0 ? all.length - 1 : proposed;
156
- })();
157
- return (_all$nextIndex = all[nextIndex]) !== null && _all$nextIndex !== void 0 ? _all$nextIndex : null;
158
- }
@@ -1 +0,0 @@
1
- export { getFirstFocusable, getLastFocusable, getNextFocusable } from './focus';
@@ -1,33 +0,0 @@
1
- import { PopupRoot } from './popup';
2
- import { PopupContent } from './popup-content';
3
- import { PopupSurface } from './popup-surface';
4
- import { PopupTrigger } from './popup-trigger';
5
- import { PopupTriggerFunction } from './popup-trigger-function';
6
-
7
- /**
8
- * Compound component for rendering content in the browser's top layer
9
- * using the native Popover API (`popover="auto"`).
10
- * Placement is owned by this compound: root provides it via context, Content applies it
11
- * (CSS Anchor Positioning or JS fallback).
12
- *
13
- * For default overlay styling (background, shadow, border-radius), see `PopupSurface` in the package examples.
14
- *
15
- * @example
16
- * ```tsx
17
- * <Popup placement={{ edge: 'end' }} onClose={handleClose}>
18
- * <Popup.Trigger>
19
- * <button>Open</button>
20
- * </Popup.Trigger>
21
- * <Popup.Content role="dialog" label="My popup">
22
- * Content goes here
23
- * </Popup.Content>
24
- * </Popup>
25
- * ```
26
- */
27
- const Popup = Object.assign(PopupRoot, {
28
- Trigger: PopupTrigger,
29
- TriggerFunction: PopupTriggerFunction,
30
- Content: PopupContent,
31
- Surface: PopupSurface
32
- });
33
- export { Popup };
@@ -1,206 +0,0 @@
1
- /* popup-content.tsx generated by @compiled/babel-plugin v0.39.1 */
2
- import _extends from "@babel/runtime/helpers/extends";
3
- import { ax, ix } from "@compiled/react/runtime";
4
- import React, { forwardRef, useCallback, useLayoutEffect, useRef } from 'react';
5
- import mergeRefs from '@atlaskit/ds-lib/merge-refs';
6
- import { prefersReducedMotion } from '../internal/reduced-motion';
7
- import { roleToAriaHasPopup, shouldFocusIntoPopover } from '../internal/role-types';
8
- import { useAnchorPosition } from '../internal/use-anchor-position';
9
- import { useWidthFromAnchor } from '../internal/use-width-from-anchor';
10
- import { Popover } from '../popover/popover';
11
- import { useMaybePopupContext } from './use-maybe-popup-context';
12
-
13
- /**
14
- * Thin context wrapper that composes `Popover` + `useAnchorPosition`.
15
- *
16
- * Used inside the `<Popup>` compound - reads triggerRef, placement, isOpen,
17
- * and onClose from context. For standalone usage (tooltip, spotlight, flag),
18
- * use `Popover` + `useAnchorPosition` directly.
19
- */
20
- export const PopupContent = /*#__PURE__*/forwardRef(function PopupContent({
21
- children,
22
- role,
23
- label,
24
- labelledBy,
25
- animate,
26
- mode,
27
- widthFromAnchor = 'none',
28
- xcss,
29
- isOpen: isOpenProp,
30
- triggerRef: triggerRefProp,
31
- placement: placementProp,
32
- onClose: onCloseProp,
33
- testId: testIdProp,
34
- forceFallbackPositioning: forceFallbackPositioningProp,
35
- id: idProp,
36
- onEnterFinish,
37
- onExitFinish
38
- }, ref) {
39
- var _ref, _ctx$popoverRef;
40
- // When inside <Popup>, context provides defaults. Props always win.
41
- const ctx = useMaybePopupContext();
42
- const triggerRef = triggerRefProp !== null && triggerRefProp !== void 0 ? triggerRefProp : ctx === null || ctx === void 0 ? void 0 : ctx.triggerRef;
43
- const placement = (_ref = placementProp !== null && placementProp !== void 0 ? placementProp : ctx === null || ctx === void 0 ? void 0 : ctx.placement) !== null && _ref !== void 0 ? _ref : {};
44
- const onClose = onCloseProp !== null && onCloseProp !== void 0 ? onCloseProp : ctx === null || ctx === void 0 ? void 0 : ctx.onClose;
45
- const testId = testIdProp !== null && testIdProp !== void 0 ? testIdProp : ctx === null || ctx === void 0 ? void 0 : ctx.testId;
46
- const forceFallbackPositioning = forceFallbackPositioningProp !== null && forceFallbackPositioningProp !== void 0 ? forceFallbackPositioningProp : ctx === null || ctx === void 0 ? void 0 : ctx.forceFallbackPositioning;
47
-
48
- // isOpen: prop wins, otherwise derive from context popupState.
49
- // 'animating-open' counts as open so <Popover> stays visible during entry animation.
50
- const isOpen = isOpenProp !== null && isOpenProp !== void 0 ? isOpenProp : ctx !== null ? ctx.popupState === 'open' || ctx.popupState === 'animating-open' : false;
51
- const setPopupState = ctx === null || ctx === void 0 ? void 0 : ctx.setPopupState;
52
- const consumerOnOpenChange = ctx === null || ctx === void 0 ? void 0 : ctx.onOpenChange;
53
-
54
- // ── TPopupState ──
55
- //
56
- // popupState is driven by handleOpenChange (below), which fires on every browser
57
- // toggle event (showPopover / hidePopover / light-dismiss).
58
- //
59
- // Exception — initial mount with isOpen=true (prop-controlled usage):
60
- // handleOpenChange only fires from the Popover's toggle event listener, which
61
- // is registered in a useEffect. On the first render, useEffect hasn't run yet,
62
- // so handleOpenChange never fires. We sync popupState here instead.
63
- //
64
- // Transitions when animations are active:
65
- // closed → animating-open → open (enter)
66
- // open → animating-closed → closed (exit, via onExitFinish)
67
- //
68
- // Without animation: closed ↔ open directly.
69
- const willAnimate = Boolean(animate) && !prefersReducedMotion();
70
- const hasMountedRef = useRef(false);
71
- useLayoutEffect(() => {
72
- if (hasMountedRef.current) {
73
- return;
74
- }
75
- hasMountedRef.current = true;
76
- if (isOpen) {
77
- setPopupState === null || setPopupState === void 0 ? void 0 : setPopupState(willAnimate ? 'animating-open' : 'open');
78
- }
79
- }, [isOpen, setPopupState, willAnimate]);
80
- const handleEnterFinish = useCallback(() => {
81
- setPopupState === null || setPopupState === void 0 ? void 0 : setPopupState('open');
82
- onEnterFinish === null || onEnterFinish === void 0 ? void 0 : onEnterFinish();
83
- }, [setPopupState, onEnterFinish]);
84
- const handleExitFinish = useCallback(() => {
85
- setPopupState === null || setPopupState === void 0 ? void 0 : setPopupState('closed');
86
- onExitFinish === null || onExitFinish === void 0 ? void 0 : onExitFinish();
87
- }, [setPopupState, onExitFinish]);
88
-
89
- // Note: onOpenChange composition is handled by handleOpenChange below,
90
- // which also adds nested-popover focus restoration.
91
-
92
- // Sync aria-haspopup on the trigger with the content's role
93
- const setAriaHasPopup = ctx === null || ctx === void 0 ? void 0 : ctx.setAriaHasPopup;
94
- useLayoutEffect(() => {
95
- setAriaHasPopup === null || setAriaHasPopup === void 0 ? void 0 : setAriaHasPopup(roleToAriaHasPopup({
96
- role
97
- }));
98
- }, [role, setAriaHasPopup]);
99
-
100
- // Focus restoration
101
- // The native Popover API handles focus restoration automatically for
102
- // popover="auto" and popover="hint":
103
- // - Escape → restores focus to the previously focused element (the trigger)
104
- // - Click-outside → does NOT restore (correct: the user clicked deliberately)
105
- // - hidePopover() → restores focus to the previously focused element
106
- //
107
- // However, for **nested** popover="auto" popovers, the browser only captures
108
- // `previouslyFocusedElement` for the outermost popover in the auto stack.
109
- // Inner popovers have `shouldRestoreFocus=false` per the spec, so the browser
110
- // does not restore focus when they close.
111
- //
112
- // To handle this, we listen for the close transition via `onOpenChange` and
113
- // restore focus to the trigger if the browser did not. This runs after the
114
- // browser's native restoration (which happens synchronously during hidePopover),
115
- // so it only takes effect when the browser skipped restoration (nested case).
116
- //
117
- // See: notes/architecture/focus-restoration.md
118
- const handleOpenChange = useCallback(args => {
119
- // Sync compound state
120
- if (args.isOpen) {
121
- setPopupState === null || setPopupState === void 0 ? void 0 : setPopupState(willAnimate ? 'animating-open' : 'open');
122
- } else {
123
- setPopupState === null || setPopupState === void 0 ? void 0 : setPopupState(willAnimate ? 'animating-closed' : 'closed');
124
- }
125
- consumerOnOpenChange === null || consumerOnOpenChange === void 0 ? void 0 : consumerOnOpenChange(args);
126
-
127
- // Nested popover focus restoration fallback
128
- // The browser only captures `previouslyFocusedElement` for the
129
- // outermost popover="auto" in the stack. Inner (nested) popovers
130
- // have shouldRestoreFocus=false, so the browser will not restore
131
- // focus when they close.
132
- //
133
- // We detect this case by checking if focus is still inside the
134
- // closing popover element. If it is, the browser did not restore,
135
- // and we need to move focus to the trigger ourselves.
136
- //
137
- // This does NOT fire for:
138
- // - Outermost popovers (browser already restored to trigger)
139
- // - Click-outside (focus is on the clicked element, not in popover)
140
- if (!args.isOpen) {
141
- const trigger = triggerRef === null || triggerRef === void 0 ? void 0 : triggerRef.current;
142
- const focusStillInPopover = trigger && args.element.contains(document.activeElement);
143
- // Single source of truth for "this role moves focus on open
144
- // and therefore needs to be restored on close" - see
145
- // `shouldFocusIntoPopover` in `internal/role-types.tsx`.
146
- if (focusStillInPopover && shouldFocusIntoPopover({
147
- role
148
- })) {
149
- trigger.focus({
150
- preventScroll: true
151
- });
152
- }
153
- }
154
- }, [consumerOnOpenChange, triggerRef, role, setPopupState, willAnimate]);
155
-
156
- // Anchor positioning
157
- // Compose useAnchorPosition with Popover. The hook positions the popover
158
- // relative to the trigger element via CSS Anchor Positioning (with JS fallback).
159
- // Use ctx.popoverRef when inside the compound (so the trigger can also
160
- // reach the same DOM element for togglePopover). Otherwise use a local ref.
161
- const localRef = useRef(null);
162
- const popoverRef = (_ctx$popoverRef = ctx === null || ctx === void 0 ? void 0 : ctx.popoverRef) !== null && _ctx$popoverRef !== void 0 ? _ctx$popoverRef : localRef;
163
- useAnchorPosition({
164
- anchorRef: triggerRef !== null && triggerRef !== void 0 ? triggerRef : {
165
- current: null
166
- },
167
- popoverRef,
168
- placement,
169
- forceFallbackPositioning
170
- });
171
- useWidthFromAnchor({
172
- mode: widthFromAnchor,
173
- popoverRef,
174
- anchorRef: triggerRef
175
- });
176
-
177
- // Merge: the popoverRef (shared with trigger + positioning) and the forwarded ref.
178
- const combinedRef = mergeRefs([popoverRef, ref]);
179
-
180
- // TPopupContentProps already validates the discriminated union (role + label,
181
- // mode + onClose) at its API boundary. Popover accepts TPopoverForwardedProps
182
- // internally so individual prop forwarding does not need to re-prove the union.
183
- const popoverProps = {
184
- isOpen,
185
- animate,
186
- placement,
187
- mode,
188
- role,
189
- label,
190
- labelledBy,
191
- onClose: onClose !== null && onClose !== void 0 ? onClose : undefined,
192
- onOpenChange: handleOpenChange,
193
- onEnterFinish: handleEnterFinish,
194
- onExitFinish: handleExitFinish,
195
- testId,
196
- children,
197
- // Standalone consumers can pass an explicit `id` - used by their own
198
- // trigger to wire `aria-controls`. Inside the `<Popup>` compound, the
199
- // id flows from context (`ctx.popoverId`).
200
- id: idProp !== null && idProp !== void 0 ? idProp : ctx === null || ctx === void 0 ? void 0 : ctx.popoverId,
201
- xcss
202
- };
203
- return /*#__PURE__*/React.createElement(Popover, _extends({
204
- ref: combinedRef
205
- }, popoverProps));
206
- });
@@ -1,20 +0,0 @@
1
- import { createContext } from 'react';
2
-
3
- /**
4
- * Represents the lifecycle state of the popup.
5
- *
6
- * - `'closed'`: fully closed, not visible, no animation playing.
7
- * - `'animating-open'`: entry animation is playing (popup is opening but not yet settled).
8
- * - `'open'`: fully open, browser popover is showing, entry animation complete.
9
- * - `'animating-closed'`: exit animation is playing (popup is closing but still visible).
10
- *
11
- * Used to derive `aria-expanded` on the trigger:
12
- * - `aria-expanded` is `true` in `'animating-open'`, `'open'`, and `'animating-closed'`.
13
- *
14
- * This avoids screen readers announcing the popup as closed while it is
15
- * still visually present on screen during animations, and prevents consumers
16
- * that conditionally show/hide trigger UI based on `aria-expanded` from hiding
17
- * the trigger (and thus the popup anchor) mid-animation.
18
- */
19
-
20
- export const PopupContext = /*#__PURE__*/createContext(null);
@@ -1,10 +0,0 @@
1
- import { PopupContext } from './popup-context';
2
-
3
- /**
4
- * __Popup provider__
5
- *
6
- * Provides shared popup state (placement, popoverId, triggerRef,
7
- * isOpen, onClose) to compound child components such as `Popup.Trigger` and
8
- * `Popup.Content`.
9
- */
10
- export const PopupProvider = PopupContext.Provider;
@@ -1,30 +0,0 @@
1
- /* popup-surface.tsx generated by @compiled/babel-plugin v0.39.1 */
2
- import "./popup-surface.compiled.css";
3
- import { ax, ix } from "@compiled/react/runtime";
4
- import React from 'react';
5
- const styles = {
6
- root: "_2rkofajl _1reo1wug _18m91wug _bfhk1bhr _16qs130s"
7
- };
8
-
9
- /**
10
- * Optional styled wrapper for popup content.
11
- *
12
- * Adds default visual styling (background, border-radius, box-shadow)
13
- * using design tokens. Consumers who want fully custom styling
14
- * should use `Popup.Content` without wrapping in `PopupSurface`.
15
- *
16
- * Exposed as `Popup.Surface` on the compound component.
17
- *
18
- * **Presentational primitive - `children`-only by design.** This component
19
- * intentionally exposes no `style`, `className`, `xcss`, `id`, or `ref`
20
- * props. If you need any of those, render your own wrapper element inside
21
- * `Popup.Content` instead - `PopupSurface` is the no-config "give me the
22
- * default ADS overlay look" surface, nothing more.
23
- */
24
- export function PopupSurface({
25
- children
26
- }) {
27
- return /*#__PURE__*/React.createElement("div", {
28
- className: ax([styles.root])
29
- }, children);
30
- }
@@ -1,75 +0,0 @@
1
- import { useCallback, useMemo } from 'react';
2
- import { usePopupContext } from './use-popup-context';
3
-
4
- /**
5
- * Props passed to the `TriggerFunction` render-prop child.
6
- */
7
-
8
- /**
9
- * Render-prop alternative to `Popup.Trigger`.
10
- *
11
- * Use when you need full control over the trigger element. For example,
12
- * to compose your own trigger with custom event handlers, wrap a third-party
13
- * component, or build a trigger from multiple elements.
14
- *
15
- * `Popup.Trigger` uses `cloneElement` internally, which limits composability.
16
- * `Popup.TriggerFunction` hands you the raw props and lets you wire them up.
17
- *
18
- * **Note:** This sub-component uses a render-prop pattern and is not
19
- * compatible with React Server Components (RSC). It must be used inside
20
- * a client component boundary (`'use client'`).
21
- *
22
- * @example
23
- * ```tsx
24
- * <Popup placement={{ edge: 'end' }} onClose={handleClose}>
25
- * <Popup.TriggerFunction>
26
- * {({ ref, toggle, ariaAttributes }) => (
27
- * <button ref={ref} onClick={toggle} {...ariaAttributes}>
28
- * Open
29
- * </button>
30
- * )}
31
- * </Popup.TriggerFunction>
32
- * <Popup.Content role="dialog" label="My popup">
33
- * Content
34
- * </Popup.Content>
35
- * </Popup>
36
- * ```
37
- */
38
- export function PopupTriggerFunction({
39
- children
40
- }) {
41
- const {
42
- triggerRef,
43
- popoverRef,
44
- popoverId,
45
- popupState,
46
- ariaHasPopup
47
- } = usePopupContext();
48
- const ref = useCallback(node => {
49
- triggerRef.current = node;
50
- }, [triggerRef]);
51
- const toggle = useCallback(() => {
52
- const popoverEl = popoverRef.current;
53
- if (popoverEl) {
54
- // Defensive: element may be disconnected or in an unexpected state.
55
- try {
56
- popoverEl.togglePopover();
57
- } catch {}
58
- }
59
- }, [popoverRef]);
60
- const ariaAttributes = useMemo(() => ({
61
- // aria-expanded stays true throughout entry and exit animations so screen readers
62
- // don't announce the popup as closed while it's still visible, and consumers
63
- // relying on aria-expanded to show/hide trigger UI don't lose the anchor mid-animation.
64
- 'aria-expanded': popupState === 'open' || popupState === 'animating-open' || popupState === 'animating-closed',
65
- 'aria-controls': popoverId,
66
- 'aria-haspopup': ariaHasPopup
67
- }), [popupState, popoverId, ariaHasPopup]);
68
- return children({
69
- ref,
70
- isOpen: popupState === 'open' || popupState === 'animating-open',
71
- popoverId,
72
- toggle,
73
- ariaAttributes
74
- });
75
- }
@@ -1,61 +0,0 @@
1
- import React, { isValidElement, useCallback } from 'react';
2
- import { Slot } from '../internal/slot';
3
- import { usePopupContext } from './use-popup-context';
4
-
5
- /**
6
- * Wraps a single trigger element via `cloneElement`. Attaches aria
7
- * attributes, the toggle handler, and the anchor-name. `aria-haspopup`
8
- * is derived from `Popup.Content`'s role via context.
9
- *
10
- * Child constraints: must be a single ref-forwarding element. The child's
11
- * `onClick` is composed (runs after); other event handlers are NOT.
12
- * Children using Compiled's `css` prop swallow injected props - use
13
- * `Popup.TriggerFunction` for these cases or any time you need handler
14
- * composition.
15
- */
16
- export function PopupTrigger({
17
- children
18
- }) {
19
- const {
20
- triggerRef,
21
- popoverRef,
22
- popoverId,
23
- popupState,
24
- ariaHasPopup
25
- } = usePopupContext();
26
- const childOnClick = /*#__PURE__*/isValidElement(children) ? children.props.onClick : undefined;
27
-
28
- // Use a ref to track the latest isOpen value so the click handler
29
- // does not need isOpen in its dependency array (which would recreate
30
- // the handler on every open/close, breaking cloneElement identity).
31
- const isOpenRef = React.useRef(popupState === 'open' || popupState === 'animating-open');
32
- isOpenRef.current = popupState === 'open' || popupState === 'animating-open';
33
- const handleClick = useCallback(event => {
34
- const popoverEl = popoverRef.current;
35
- if (popoverEl) {
36
- // `popover="auto"` light-dismiss can close the popover BEFORE
37
- // our click handler runs, so trust React's isOpen rather than
38
- // DOM state. If React still thinks it is closed, open it; if
39
- // it was open, the browser already closed it.
40
- if (!isOpenRef.current) {
41
- try {
42
- popoverEl.showPopover();
43
- } catch {
44
- // Element may be disconnected; ignore.
45
- }
46
- }
47
- }
48
- childOnClick === null || childOnClick === void 0 ? void 0 : childOnClick(event);
49
- }, [popoverRef, childOnClick]);
50
- return /*#__PURE__*/React.createElement(Slot, {
51
- ref: triggerRef,
52
- onClick: handleClick
53
- // aria-expanded stays true throughout entry and exit animations so screen readers
54
- // don't announce the popup as closed while it's still visible, and consumers
55
- // relying on aria-expanded to show/hide trigger UI don't lose the anchor mid-animation.
56
- ,
57
- "aria-expanded": popupState === 'open' || popupState === 'animating-open' || popupState === 'animating-closed',
58
- "aria-controls": popoverId,
59
- "aria-haspopup": ariaHasPopup
60
- }, children);
61
- }
@@ -1,67 +0,0 @@
1
- import React, { useCallback, useId, useRef, useState } from 'react';
2
- import { PopupProvider } from './popup-provider';
3
- /**
4
- * Popup compound: trigger + popover content.
5
- *
6
- * Use when a button opens anchored content (dropdown, menu, dialog).
7
- * The browser manages visibility via `togglePopover()`; the consumer
8
- * never manages `isOpen`. For custom trigger lifecycles (hover, timers,
9
- * external state), use `Popover` directly instead.
10
- *
11
- * Provides context (triggerRef, placement, isOpen, onClose, mode) that
12
- * `Popup.Content` reads and forwards to the underlying `Popover`.
13
- */
14
- export function PopupRoot(props) {
15
- var _props$placement, _props$mode;
16
- const {
17
- children,
18
- testId,
19
- forceFallbackPositioning,
20
- onOpenChange
21
- } = props;
22
- // `{}` resolves to "below the trigger, centered, with `space.100` gap"
23
- // inside `useAnchorPosition`, which is the common case.
24
- const placement = (_props$placement = props.placement) !== null && _props$placement !== void 0 ? _props$placement : {};
25
- const mode = (_props$mode = props.mode) !== null && _props$mode !== void 0 ? _props$mode : 'auto';
26
-
27
- // `TPopupProps` discriminates on `mode`: `manual` types `onClose` as
28
- // `never`, so for manual consumers `props.onClose` is always `undefined`
29
- // here. No runtime guard needed; the type contract carries the rule.
30
- const onClose = props.onClose;
31
- const id = useId();
32
- // `useId()` returns IDs containing colons (`:r1:`), which are invalid
33
- // in CSS selectors and `popover` target attributes. `replaceAll` is
34
- // not in our browser matrix.
35
- const sanitizedId = id.replace(/:/g, '');
36
- const popoverId = `popover-${sanitizedId}`;
37
- const triggerRef = useRef(null);
38
- const popoverRef = useRef(null);
39
- const [popupState, setPopupState] = useState('closed');
40
- // Initial seed before `Popup.Content`'s layout effect resolves the
41
- // real role on first paint.
42
- const [ariaHasPopup, setAriaHasPopup] = useState('dialog');
43
- const handleClose = useCallback(args => {
44
- onClose === null || onClose === void 0 ? void 0 : onClose(args);
45
- }, [onClose]);
46
- const contextValue = {
47
- popoverId,
48
- placement,
49
- // `null` when the consumer did not supply a handler (manual mode, or
50
- // auto/hint without a light-dismiss handler). `Popup.Content` reads
51
- // this to decide whether to forward an `onClose` to `Popover`.
52
- onClose: onClose ? handleClose : null,
53
- triggerRef,
54
- popoverRef,
55
- popupState,
56
- setPopupState,
57
- onOpenChange,
58
- mode,
59
- ariaHasPopup,
60
- setAriaHasPopup,
61
- testId,
62
- forceFallbackPositioning
63
- };
64
- return /*#__PURE__*/React.createElement(PopupProvider, {
65
- value: contextValue
66
- }, children);
67
- }
@@ -1 +0,0 @@
1
- export {};