@atlaskit/top-layer 0.1.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 (300) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +151 -0
  3. package/animations/package.json +17 -0
  4. package/arrow/package.json +17 -0
  5. package/create-close-event/package.json +17 -0
  6. package/dialog/package.json +17 -0
  7. package/dialog-scroll-lock/package.json +17 -0
  8. package/dist/cjs/animations/presets.js +199 -0
  9. package/dist/cjs/animations/types.js +5 -0
  10. package/dist/cjs/arrow/index.js +130 -0
  11. package/dist/cjs/arrow/types.js +5 -0
  12. package/dist/cjs/dialog/create-close-event.js +39 -0
  13. package/dist/cjs/dialog/dialog-content.compiled.css +14 -0
  14. package/dist/cjs/dialog/dialog-content.js +144 -0
  15. package/dist/cjs/dialog/index.js +19 -0
  16. package/dist/cjs/dialog/types.js +5 -0
  17. package/dist/cjs/dialog-scroll-lock/index.js +46 -0
  18. package/dist/cjs/entry-points/animations.js +36 -0
  19. package/dist/cjs/entry-points/arrow.js +12 -0
  20. package/dist/cjs/entry-points/create-close-event.js +19 -0
  21. package/dist/cjs/entry-points/dialog-scroll-lock.js +12 -0
  22. package/dist/cjs/entry-points/dialog.js +12 -0
  23. package/dist/cjs/entry-points/focus.js +24 -0
  24. package/dist/cjs/entry-points/placement-map.js +18 -0
  25. package/dist/cjs/entry-points/popover.js +12 -0
  26. package/dist/cjs/entry-points/popup-surface.js +12 -0
  27. package/dist/cjs/entry-points/popup.js +12 -0
  28. package/dist/cjs/entry-points/use-anchor-position.js +12 -0
  29. package/dist/cjs/entry-points/use-arrow-navigation.js +18 -0
  30. package/dist/cjs/entry-points/use-simple-light-dismiss.js +12 -0
  31. package/dist/cjs/focus/focus.js +115 -0
  32. package/dist/cjs/focus/index.js +24 -0
  33. package/dist/cjs/index.js +8 -0
  34. package/dist/cjs/internal/anchor-positioning-fallback.js +154 -0
  35. package/dist/cjs/internal/combine.js +29 -0
  36. package/dist/cjs/internal/ensure-preset-styles.js +34 -0
  37. package/dist/cjs/internal/reduced-motion.js +27 -0
  38. package/dist/cjs/internal/resolve-placement.js +24 -0
  39. package/dist/cjs/internal/role-types.js +66 -0
  40. package/dist/cjs/internal/set-style.js +42 -0
  41. package/dist/cjs/internal/slot.js +65 -0
  42. package/dist/cjs/internal/use-anchor-positioning.js +336 -0
  43. package/dist/cjs/internal/use-animated-visibility.js +169 -0
  44. package/dist/cjs/internal/use-animation-preset.js +24 -0
  45. package/dist/cjs/internal/use-focus-wrap.js +94 -0
  46. package/dist/cjs/internal/use-initial-focus.js +112 -0
  47. package/dist/cjs/internal/use-preset-styles.js +27 -0
  48. package/dist/cjs/placement-map/index.js +147 -0
  49. package/dist/cjs/popover/create-close-event.js +44 -0
  50. package/dist/cjs/popover/index.js +19 -0
  51. package/dist/cjs/popover/popover.compiled.css +13 -0
  52. package/dist/cjs/popover/popover.js +251 -0
  53. package/dist/cjs/popover/types.js +5 -0
  54. package/dist/cjs/popup/index.js +37 -0
  55. package/dist/cjs/popup/popup-content.js +201 -0
  56. package/dist/cjs/popup/popup-context.js +42 -0
  57. package/dist/cjs/popup/popup-surface.compiled.css +5 -0
  58. package/dist/cjs/popup/popup-surface.js +30 -0
  59. package/dist/cjs/popup/popup-trigger-function.js +74 -0
  60. package/dist/cjs/popup/popup-trigger.js +82 -0
  61. package/dist/cjs/popup/popup.js +70 -0
  62. package/dist/cjs/popup/types.js +5 -0
  63. package/dist/cjs/use-arrow-navigation/index.js +18 -0
  64. package/dist/cjs/use-arrow-navigation/use-arrow-navigation.js +245 -0
  65. package/dist/cjs/use-simple-light-dismiss/index.js +84 -0
  66. package/dist/es2019/animations/presets.js +375 -0
  67. package/dist/es2019/animations/types.js +1 -0
  68. package/dist/es2019/arrow/index.js +249 -0
  69. package/dist/es2019/arrow/types.js +1 -0
  70. package/dist/es2019/dialog/create-close-event.js +34 -0
  71. package/dist/es2019/dialog/dialog-content.compiled.css +14 -0
  72. package/dist/es2019/dialog/dialog-content.js +138 -0
  73. package/dist/es2019/dialog/index.js +2 -0
  74. package/dist/es2019/dialog/types.js +1 -0
  75. package/dist/es2019/dialog-scroll-lock/index.js +41 -0
  76. package/dist/es2019/entry-points/animations.js +1 -0
  77. package/dist/es2019/entry-points/arrow.js +1 -0
  78. package/dist/es2019/entry-points/create-close-event.js +2 -0
  79. package/dist/es2019/entry-points/dialog-scroll-lock.js +1 -0
  80. package/dist/es2019/entry-points/dialog.js +1 -0
  81. package/dist/es2019/entry-points/focus.js +1 -0
  82. package/dist/es2019/entry-points/placement-map.js +1 -0
  83. package/dist/es2019/entry-points/popover.js +1 -0
  84. package/dist/es2019/entry-points/popup-surface.js +1 -0
  85. package/dist/es2019/entry-points/popup.js +1 -0
  86. package/dist/es2019/entry-points/use-anchor-position.js +1 -0
  87. package/dist/es2019/entry-points/use-arrow-navigation.js +1 -0
  88. package/dist/es2019/entry-points/use-simple-light-dismiss.js +1 -0
  89. package/dist/es2019/focus/focus.js +107 -0
  90. package/dist/es2019/focus/index.js +1 -0
  91. package/dist/es2019/index.js +7 -0
  92. package/dist/es2019/internal/anchor-positioning-fallback.js +153 -0
  93. package/dist/es2019/internal/combine.js +18 -0
  94. package/dist/es2019/internal/ensure-preset-styles.js +29 -0
  95. package/dist/es2019/internal/reduced-motion.js +21 -0
  96. package/dist/es2019/internal/resolve-placement.js +19 -0
  97. package/dist/es2019/internal/role-types.js +61 -0
  98. package/dist/es2019/internal/set-style.js +39 -0
  99. package/dist/es2019/internal/slot.js +57 -0
  100. package/dist/es2019/internal/use-anchor-positioning.js +330 -0
  101. package/dist/es2019/internal/use-animated-visibility.js +159 -0
  102. package/dist/es2019/internal/use-animation-preset.js +18 -0
  103. package/dist/es2019/internal/use-focus-wrap.js +90 -0
  104. package/dist/es2019/internal/use-initial-focus.js +109 -0
  105. package/dist/es2019/internal/use-preset-styles.js +22 -0
  106. package/dist/es2019/placement-map/index.js +142 -0
  107. package/dist/es2019/popover/create-close-event.js +39 -0
  108. package/dist/es2019/popover/index.js +2 -0
  109. package/dist/es2019/popover/popover.compiled.css +13 -0
  110. package/dist/es2019/popover/popover.js +240 -0
  111. package/dist/es2019/popover/types.js +1 -0
  112. package/dist/es2019/popup/index.js +33 -0
  113. package/dist/es2019/popup/popup-content.js +192 -0
  114. package/dist/es2019/popup/popup-context.js +34 -0
  115. package/dist/es2019/popup/popup-surface.compiled.css +5 -0
  116. package/dist/es2019/popup/popup-surface.js +24 -0
  117. package/dist/es2019/popup/popup-trigger-function.js +69 -0
  118. package/dist/es2019/popup/popup-trigger.js +76 -0
  119. package/dist/es2019/popup/popup.js +56 -0
  120. package/dist/es2019/popup/types.js +1 -0
  121. package/dist/es2019/use-arrow-navigation/index.js +1 -0
  122. package/dist/es2019/use-arrow-navigation/use-arrow-navigation.js +238 -0
  123. package/dist/es2019/use-simple-light-dismiss/index.js +79 -0
  124. package/dist/esm/animations/presets.js +190 -0
  125. package/dist/esm/animations/types.js +1 -0
  126. package/dist/esm/arrow/index.js +123 -0
  127. package/dist/esm/arrow/types.js +1 -0
  128. package/dist/esm/dialog/create-close-event.js +33 -0
  129. package/dist/esm/dialog/dialog-content.compiled.css +14 -0
  130. package/dist/esm/dialog/dialog-content.js +135 -0
  131. package/dist/esm/dialog/index.js +2 -0
  132. package/dist/esm/dialog/types.js +1 -0
  133. package/dist/esm/dialog-scroll-lock/index.js +41 -0
  134. package/dist/esm/entry-points/animations.js +1 -0
  135. package/dist/esm/entry-points/arrow.js +1 -0
  136. package/dist/esm/entry-points/create-close-event.js +2 -0
  137. package/dist/esm/entry-points/dialog-scroll-lock.js +1 -0
  138. package/dist/esm/entry-points/dialog.js +1 -0
  139. package/dist/esm/entry-points/focus.js +1 -0
  140. package/dist/esm/entry-points/placement-map.js +1 -0
  141. package/dist/esm/entry-points/popover.js +1 -0
  142. package/dist/esm/entry-points/popup-surface.js +1 -0
  143. package/dist/esm/entry-points/popup.js +1 -0
  144. package/dist/esm/entry-points/use-anchor-position.js +1 -0
  145. package/dist/esm/entry-points/use-arrow-navigation.js +1 -0
  146. package/dist/esm/entry-points/use-simple-light-dismiss.js +1 -0
  147. package/dist/esm/focus/focus.js +107 -0
  148. package/dist/esm/focus/index.js +1 -0
  149. package/dist/esm/index.js +7 -0
  150. package/dist/esm/internal/anchor-positioning-fallback.js +147 -0
  151. package/dist/esm/internal/combine.js +23 -0
  152. package/dist/esm/internal/ensure-preset-styles.js +28 -0
  153. package/dist/esm/internal/reduced-motion.js +21 -0
  154. package/dist/esm/internal/resolve-placement.js +18 -0
  155. package/dist/esm/internal/role-types.js +60 -0
  156. package/dist/esm/internal/set-style.js +36 -0
  157. package/dist/esm/internal/slot.js +59 -0
  158. package/dist/esm/internal/use-anchor-positioning.js +323 -0
  159. package/dist/esm/internal/use-animated-visibility.js +162 -0
  160. package/dist/esm/internal/use-animation-preset.js +18 -0
  161. package/dist/esm/internal/use-focus-wrap.js +89 -0
  162. package/dist/esm/internal/use-initial-focus.js +107 -0
  163. package/dist/esm/internal/use-preset-styles.js +21 -0
  164. package/dist/esm/placement-map/index.js +141 -0
  165. package/dist/esm/popover/create-close-event.js +38 -0
  166. package/dist/esm/popover/index.js +2 -0
  167. package/dist/esm/popover/popover.compiled.css +13 -0
  168. package/dist/esm/popover/popover.js +242 -0
  169. package/dist/esm/popover/types.js +1 -0
  170. package/dist/esm/popup/index.js +33 -0
  171. package/dist/esm/popup/popup-content.js +192 -0
  172. package/dist/esm/popup/popup-context.js +34 -0
  173. package/dist/esm/popup/popup-surface.compiled.css +5 -0
  174. package/dist/esm/popup/popup-surface.js +23 -0
  175. package/dist/esm/popup/popup-trigger-function.js +69 -0
  176. package/dist/esm/popup/popup-trigger.js +74 -0
  177. package/dist/esm/popup/popup.js +61 -0
  178. package/dist/esm/popup/types.js +1 -0
  179. package/dist/esm/use-arrow-navigation/index.js +1 -0
  180. package/dist/esm/use-arrow-navigation/use-arrow-navigation.js +238 -0
  181. package/dist/esm/use-simple-light-dismiss/index.js +78 -0
  182. package/dist/types/animations/presets.d.ts +94 -0
  183. package/dist/types/animations/types.d.ts +42 -0
  184. package/dist/types/arrow/index.d.ts +24 -0
  185. package/dist/types/arrow/types.d.ts +29 -0
  186. package/dist/types/dialog/create-close-event.d.ts +24 -0
  187. package/dist/types/dialog/dialog-content.d.ts +20 -0
  188. package/dist/types/dialog/index.d.ts +3 -0
  189. package/dist/types/dialog/types.d.ts +92 -0
  190. package/dist/types/dialog-scroll-lock/index.d.ts +24 -0
  191. package/dist/types/entry-points/animations.d.ts +2 -0
  192. package/dist/types/entry-points/arrow.d.ts +2 -0
  193. package/dist/types/entry-points/create-close-event.d.ts +4 -0
  194. package/dist/types/entry-points/dialog-scroll-lock.d.ts +1 -0
  195. package/dist/types/entry-points/dialog.d.ts +2 -0
  196. package/dist/types/entry-points/focus.d.ts +2 -0
  197. package/dist/types/entry-points/placement-map.d.ts +2 -0
  198. package/dist/types/entry-points/popover.d.ts +2 -0
  199. package/dist/types/entry-points/popup-surface.d.ts +2 -0
  200. package/dist/types/entry-points/popup.d.ts +2 -0
  201. package/dist/types/entry-points/use-anchor-position.d.ts +1 -0
  202. package/dist/types/entry-points/use-arrow-navigation.d.ts +2 -0
  203. package/dist/types/entry-points/use-simple-light-dismiss.d.ts +2 -0
  204. package/dist/types/focus/focus.d.ts +42 -0
  205. package/dist/types/focus/index.d.ts +2 -0
  206. package/dist/types/index.d.ts +0 -0
  207. package/dist/types/internal/anchor-positioning-fallback.d.ts +24 -0
  208. package/dist/types/internal/combine.d.ts +14 -0
  209. package/dist/types/internal/ensure-preset-styles.d.ts +6 -0
  210. package/dist/types/internal/reduced-motion.d.ts +14 -0
  211. package/dist/types/internal/resolve-placement.d.ts +16 -0
  212. package/dist/types/internal/role-types.d.ts +62 -0
  213. package/dist/types/internal/set-style.d.ts +28 -0
  214. package/dist/types/internal/slot.d.ts +29 -0
  215. package/dist/types/internal/use-anchor-positioning.d.ts +95 -0
  216. package/dist/types/internal/use-animated-visibility.d.ts +74 -0
  217. package/dist/types/internal/use-animation-preset.d.ts +11 -0
  218. package/dist/types/internal/use-focus-wrap.d.ts +20 -0
  219. package/dist/types/internal/use-initial-focus.d.ts +21 -0
  220. package/dist/types/internal/use-preset-styles.d.ts +15 -0
  221. package/dist/types/placement-map/index.d.ts +55 -0
  222. package/dist/types/popover/create-close-event.d.ts +26 -0
  223. package/dist/types/popover/index.d.ts +3 -0
  224. package/dist/types/popover/popover.d.ts +22 -0
  225. package/dist/types/popover/types.d.ts +153 -0
  226. package/dist/types/popup/index.d.ts +34 -0
  227. package/dist/types/popup/popup-content.d.ts +14 -0
  228. package/dist/types/popup/popup-context.d.ts +96 -0
  229. package/dist/types/popup/popup-surface.d.ts +16 -0
  230. package/dist/types/popup/popup-trigger-function.d.ts +108 -0
  231. package/dist/types/popup/popup-trigger.d.ts +23 -0
  232. package/dist/types/popup/popup.d.ts +14 -0
  233. package/dist/types/popup/types.d.ts +289 -0
  234. package/dist/types/use-arrow-navigation/index.d.ts +2 -0
  235. package/dist/types/use-arrow-navigation/use-arrow-navigation.d.ts +136 -0
  236. package/dist/types/use-simple-light-dismiss/index.d.ts +45 -0
  237. package/dist/types-ts4.5/animations/presets.d.ts +94 -0
  238. package/dist/types-ts4.5/animations/types.d.ts +42 -0
  239. package/dist/types-ts4.5/arrow/index.d.ts +24 -0
  240. package/dist/types-ts4.5/arrow/types.d.ts +29 -0
  241. package/dist/types-ts4.5/dialog/create-close-event.d.ts +24 -0
  242. package/dist/types-ts4.5/dialog/dialog-content.d.ts +20 -0
  243. package/dist/types-ts4.5/dialog/index.d.ts +3 -0
  244. package/dist/types-ts4.5/dialog/types.d.ts +92 -0
  245. package/dist/types-ts4.5/dialog-scroll-lock/index.d.ts +24 -0
  246. package/dist/types-ts4.5/entry-points/animations.d.ts +2 -0
  247. package/dist/types-ts4.5/entry-points/arrow.d.ts +2 -0
  248. package/dist/types-ts4.5/entry-points/create-close-event.d.ts +4 -0
  249. package/dist/types-ts4.5/entry-points/dialog-scroll-lock.d.ts +1 -0
  250. package/dist/types-ts4.5/entry-points/dialog.d.ts +2 -0
  251. package/dist/types-ts4.5/entry-points/focus.d.ts +2 -0
  252. package/dist/types-ts4.5/entry-points/placement-map.d.ts +2 -0
  253. package/dist/types-ts4.5/entry-points/popover.d.ts +2 -0
  254. package/dist/types-ts4.5/entry-points/popup-surface.d.ts +2 -0
  255. package/dist/types-ts4.5/entry-points/popup.d.ts +2 -0
  256. package/dist/types-ts4.5/entry-points/use-anchor-position.d.ts +1 -0
  257. package/dist/types-ts4.5/entry-points/use-arrow-navigation.d.ts +2 -0
  258. package/dist/types-ts4.5/entry-points/use-simple-light-dismiss.d.ts +2 -0
  259. package/dist/types-ts4.5/focus/focus.d.ts +42 -0
  260. package/dist/types-ts4.5/focus/index.d.ts +2 -0
  261. package/dist/types-ts4.5/index.d.ts +0 -0
  262. package/dist/types-ts4.5/internal/anchor-positioning-fallback.d.ts +24 -0
  263. package/dist/types-ts4.5/internal/combine.d.ts +14 -0
  264. package/dist/types-ts4.5/internal/ensure-preset-styles.d.ts +6 -0
  265. package/dist/types-ts4.5/internal/reduced-motion.d.ts +14 -0
  266. package/dist/types-ts4.5/internal/resolve-placement.d.ts +16 -0
  267. package/dist/types-ts4.5/internal/role-types.d.ts +62 -0
  268. package/dist/types-ts4.5/internal/set-style.d.ts +28 -0
  269. package/dist/types-ts4.5/internal/slot.d.ts +29 -0
  270. package/dist/types-ts4.5/internal/use-anchor-positioning.d.ts +95 -0
  271. package/dist/types-ts4.5/internal/use-animated-visibility.d.ts +74 -0
  272. package/dist/types-ts4.5/internal/use-animation-preset.d.ts +11 -0
  273. package/dist/types-ts4.5/internal/use-focus-wrap.d.ts +20 -0
  274. package/dist/types-ts4.5/internal/use-initial-focus.d.ts +21 -0
  275. package/dist/types-ts4.5/internal/use-preset-styles.d.ts +15 -0
  276. package/dist/types-ts4.5/placement-map/index.d.ts +55 -0
  277. package/dist/types-ts4.5/popover/create-close-event.d.ts +26 -0
  278. package/dist/types-ts4.5/popover/index.d.ts +3 -0
  279. package/dist/types-ts4.5/popover/popover.d.ts +22 -0
  280. package/dist/types-ts4.5/popover/types.d.ts +153 -0
  281. package/dist/types-ts4.5/popup/index.d.ts +34 -0
  282. package/dist/types-ts4.5/popup/popup-content.d.ts +14 -0
  283. package/dist/types-ts4.5/popup/popup-context.d.ts +96 -0
  284. package/dist/types-ts4.5/popup/popup-surface.d.ts +16 -0
  285. package/dist/types-ts4.5/popup/popup-trigger-function.d.ts +108 -0
  286. package/dist/types-ts4.5/popup/popup-trigger.d.ts +23 -0
  287. package/dist/types-ts4.5/popup/popup.d.ts +14 -0
  288. package/dist/types-ts4.5/popup/types.d.ts +289 -0
  289. package/dist/types-ts4.5/use-arrow-navigation/index.d.ts +2 -0
  290. package/dist/types-ts4.5/use-arrow-navigation/use-arrow-navigation.d.ts +136 -0
  291. package/dist/types-ts4.5/use-simple-light-dismiss/index.d.ts +45 -0
  292. package/focus/package.json +17 -0
  293. package/package.json +95 -0
  294. package/placement-map/package.json +17 -0
  295. package/popover/package.json +17 -0
  296. package/popup/package.json +17 -0
  297. package/popup-surface/package.json +17 -0
  298. package/use-anchor-position/package.json +17 -0
  299. package/use-arrow-navigation/package.json +17 -0
  300. package/use-simple-light-dismiss/package.json +17 -0
@@ -0,0 +1,109 @@
1
+ import { useEffect } from 'react';
2
+ import { getFirstFocusable } from '../focus/focus';
3
+
4
+ /**
5
+ * Determines the correct element to receive initial focus within a popover,
6
+ * based on its ARIA role.
7
+ *
8
+ * Focus rules by role (per WAI-ARIA APG):
9
+ * - `dialog` / `alertdialog`: first element with `[autofocus]`, or first focusable element
10
+ * - `menu`: first menu item (`[role="menuitem|menuitemcheckbox|menuitemradio"]`)
11
+ * - `listbox`: first selected option (`[aria-selected="true"]`), or first option (`[role="option"]`)
12
+ * - `tooltip`: no focus movement (tooltips are informational)
13
+ * - no role / other roles: no focus movement
14
+ *
15
+ * Note: React 18's `autoFocus` JSX prop does NOT set the HTML `autofocus`
16
+ * attribute — it only calls `.focus()` imperatively during commit, which
17
+ * has no effect on hidden popover content. Consumers must set the native
18
+ * attribute via a ref callback (e.g. `node.setAttribute('autofocus', '')`).
19
+ * React 19+ reflects the JSX prop as the HTML attribute automatically.
20
+ */
21
+ function getInitialFocusTarget({
22
+ container
23
+ }) {
24
+ const role = container.getAttribute('role');
25
+ if (role === 'dialog' || role === 'alertdialog') {
26
+ // Prefer element with the native HTML autofocus attribute.
27
+ const autofocusEl = container.querySelector('[autofocus]');
28
+ if (autofocusEl) {
29
+ return autofocusEl;
30
+ }
31
+ // Fall back to first focusable element
32
+ return getFirstFocusable({
33
+ container
34
+ });
35
+ }
36
+ if (role === 'menu') {
37
+ // Initial focus placement is the extent of what top-layer provides
38
+ // for menus. All subsequent keyboard navigation (arrow keys, Home/End,
39
+ // type-ahead, Enter/Space, submenu coordination) is the responsibility
40
+ // of the consumer component (e.g. dropdown-menu), because menu keyboard
41
+ // behavior varies by context (orientation, nesting, item type, control
42
+ // pattern). See: notes/outputs/menu-keyboard-decision.md
43
+ return getFirstFocusable({
44
+ container
45
+ });
46
+ }
47
+ if (role === 'listbox') {
48
+ // Prefer selected option, fall back to first focusable
49
+ const selectedOption = container.querySelector('[role="option"][aria-selected="true"]');
50
+ if (selectedOption) {
51
+ return selectedOption;
52
+ }
53
+ return getFirstFocusable({
54
+ container
55
+ });
56
+ }
57
+
58
+ // tooltip, no role, or other roles: no focus movement
59
+ return null;
60
+ }
61
+
62
+ /**
63
+ * Moves focus into a popover element when it opens, based on its ARIA role.
64
+ *
65
+ * This implements WCAG 2.4.3 (Focus Order) initial focus placement:
66
+ * - `dialog` / `alertdialog` → focus first focusable (or `[autofocus]`)
67
+ * - `menu` → focus first menu item
68
+ * - `listbox` → focus selected or first option
69
+ * - `tooltip` / no role → no focus movement
70
+ *
71
+ * The role is read from the element's `role` attribute (not passed as a prop),
72
+ * following the same pattern as `useFocusWrap`.
73
+ *
74
+ * Always call this hook unconditionally. Focus is only moved when `isOpen`
75
+ * transitions to `true` and the role requires it.
76
+ */
77
+ export function useInitialFocus({
78
+ elementRef,
79
+ isOpen,
80
+ role
81
+ }) {
82
+ useEffect(() => {
83
+ if (!isOpen) {
84
+ return;
85
+ }
86
+ const el = elementRef.current;
87
+ if (!el) {
88
+ return;
89
+ }
90
+
91
+ // Use requestAnimationFrame to ensure the popover is fully rendered
92
+ // and visible in the top layer before attempting to move focus.
93
+ // showPopover() is called in useLayoutEffect (which runs before this
94
+ // useEffect), so by the time the RAF fires after the next paint the
95
+ // element is guaranteed to be in the top layer.
96
+ const rafId = requestAnimationFrame(() => {
97
+ var _getInitialFocusTarge;
98
+ (_getInitialFocusTarge = getInitialFocusTarget({
99
+ container: el
100
+ })) === null || _getInitialFocusTarge === void 0 ? void 0 : _getInitialFocusTarge.focus();
101
+ });
102
+ return () => {
103
+ cancelAnimationFrame(rafId);
104
+ };
105
+ // We intentionally depend on `role` even though `getInitialFocusTarget`
106
+ // reads from the DOM attribute. This ensures the effect re-runs if
107
+ // the role prop changes while the popover is open.
108
+ }, [elementRef, isOpen, role]);
109
+ }
@@ -0,0 +1,22 @@
1
+ import { useInsertionEffect } from 'react';
2
+ import { ensurePresetStyles } from './ensure-preset-styles';
3
+ /**
4
+ * Normalizes a preset prop to a value (or null) and ensures its CSS
5
+ * is injected once into `document.head`.
6
+ *
7
+ * Works with any preset that has `{ css, name }` — animation presets
8
+ * (TAnimationPreset) and arrow presets (TArrowPreset) both satisfy this shape.
9
+ */
10
+ export function usePresetStyles({
11
+ preset
12
+ }) {
13
+ const resolved = preset != null && preset !== false ? preset : null;
14
+ useInsertionEffect(() => {
15
+ if (resolved) {
16
+ ensurePresetStyles({
17
+ preset: resolved
18
+ });
19
+ }
20
+ }, [resolved]);
21
+ return resolved;
22
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Legacy Popper.js placement values (used by `@atlaskit/popper`, `@atlaskit/popup`,
3
+ * `@atlaskit/tooltip`, etc.).
4
+ */
5
+
6
+ /**
7
+ * Maps legacy Popper.js placement values to the new object-based
8
+ * `Placement` values used by `@atlaskit/top-layer/popover`.
9
+ *
10
+ * **Mapping logic:**
11
+ *
12
+ * | Legacy (Popper.js) | New (Placement object) |
13
+ * | ------------------- | --------------------------------------------------- |
14
+ * | `top` | `{ axis: 'block', edge: 'start', align: 'center' }` |
15
+ * | `top-start` | `{ axis: 'block', edge: 'start', align: 'start' }` |
16
+ * | `top-end` | `{ axis: 'block', edge: 'start', align: 'end' }` |
17
+ * | `bottom` | `{ axis: 'block', edge: 'end', align: 'center' }` |
18
+ * | `bottom-start` | `{ axis: 'block', edge: 'end', align: 'start' }` |
19
+ * | `bottom-end` | `{ axis: 'block', edge: 'end', align: 'end' }` |
20
+ * | `right` | `{ axis: 'inline', edge: 'end', align: 'center' }` |
21
+ * | `right-start` | `{ axis: 'inline', edge: 'end', align: 'start' }` |
22
+ * | `right-end` | `{ axis: 'inline', edge: 'end', align: 'end' }` |
23
+ * | `left` | `{ axis: 'inline', edge: 'start', align: 'center' }` |
24
+ * | `left-start` | `{ axis: 'inline', edge: 'start', align: 'start' }` |
25
+ * | `left-end` | `{ axis: 'inline', edge: 'start', align: 'end' }` |
26
+ * | `auto` | `{ axis: 'block', edge: 'end', align: 'center' }` |
27
+ * | `auto-start` | `{ axis: 'block', edge: 'end', align: 'start' }` |
28
+ * | `auto-end` | `{ axis: 'block', edge: 'end', align: 'end' }` |
29
+ *
30
+ * The `position-try-fallbacks` CSS property handles flipping automatically.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * import { fromLegacyPlacement } from '@atlaskit/top-layer/placement-map';
35
+ *
36
+ * // In a migration from @atlaskit/popup:
37
+ * const oldPlacement: TLegacyPlacement = 'bottom-start';
38
+ * const newPlacement: TPlacementOptions = fromLegacyPlacement({ legacy: oldPlacement });
39
+ * // newPlacement === { axis: 'block', edge: 'end', align: 'start' }
40
+ * ```
41
+ */
42
+ const placementMapping = {
43
+ top: {
44
+ axis: 'block',
45
+ edge: 'start',
46
+ align: 'center'
47
+ },
48
+ 'top-start': {
49
+ axis: 'block',
50
+ edge: 'start',
51
+ align: 'start'
52
+ },
53
+ 'top-center': {
54
+ axis: 'block',
55
+ edge: 'start',
56
+ align: 'center'
57
+ },
58
+ 'top-end': {
59
+ axis: 'block',
60
+ edge: 'start',
61
+ align: 'end'
62
+ },
63
+ bottom: {
64
+ axis: 'block',
65
+ edge: 'end',
66
+ align: 'center'
67
+ },
68
+ 'bottom-start': {
69
+ axis: 'block',
70
+ edge: 'end',
71
+ align: 'start'
72
+ },
73
+ 'bottom-center': {
74
+ axis: 'block',
75
+ edge: 'end',
76
+ align: 'center'
77
+ },
78
+ 'bottom-end': {
79
+ axis: 'block',
80
+ edge: 'end',
81
+ align: 'end'
82
+ },
83
+ right: {
84
+ axis: 'inline',
85
+ edge: 'end',
86
+ align: 'center'
87
+ },
88
+ 'right-start': {
89
+ axis: 'inline',
90
+ edge: 'end',
91
+ align: 'start'
92
+ },
93
+ 'right-end': {
94
+ axis: 'inline',
95
+ edge: 'end',
96
+ align: 'end'
97
+ },
98
+ left: {
99
+ axis: 'inline',
100
+ edge: 'start',
101
+ align: 'center'
102
+ },
103
+ 'left-start': {
104
+ axis: 'inline',
105
+ edge: 'start',
106
+ align: 'start'
107
+ },
108
+ 'left-end': {
109
+ axis: 'inline',
110
+ edge: 'start',
111
+ align: 'end'
112
+ },
113
+ auto: {
114
+ axis: 'block',
115
+ edge: 'end',
116
+ align: 'center'
117
+ },
118
+ 'auto-start': {
119
+ axis: 'block',
120
+ edge: 'end',
121
+ align: 'start'
122
+ },
123
+ 'auto-end': {
124
+ axis: 'block',
125
+ edge: 'end',
126
+ align: 'end'
127
+ }
128
+ };
129
+
130
+ /**
131
+ * Convert a legacy Popper.js placement to the new CSS `position-area`-based Placement.
132
+ */
133
+ export function fromLegacyPlacement({
134
+ legacy
135
+ }) {
136
+ return placementMapping[legacy];
137
+ }
138
+
139
+ /**
140
+ * The full mapping record, exposed for consumers that need to iterate or introspect.
141
+ */
142
+ export { placementMapping };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Creates a synthetic DOM event from a `TPopoverCloseReason`.
3
+ *
4
+ * Useful when bridging the Popover primitive's `onClose({ reason })` callback
5
+ * to a legacy API that expects a native DOM event — for example,
6
+ * `@atlaskit/inline-dialog`'s `onClose(event)` contract.
7
+ *
8
+ * - `'escape'` → `KeyboardEvent('keydown', { key: 'Escape' })`
9
+ * - `'light-dismiss'` → `MouseEvent('click')`
10
+ *
11
+ * For any other reason, a generic `Event('close')` is returned as a fallback.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * import { createPopoverCloseEvent } from '@atlaskit/top-layer/create-close-event';
16
+ * import { type TPopoverCloseReason } from '@atlaskit/top-layer/popover';
17
+ *
18
+ * function onPopoverClose({ reason }: { reason: TPopoverCloseReason }) {
19
+ * legacyOnClose(createPopoverCloseEvent({ reason }));
20
+ * }
21
+ * ```
22
+ */
23
+ export function createPopoverCloseEvent({
24
+ reason
25
+ }) {
26
+ if (reason === 'escape') {
27
+ return new KeyboardEvent('keydown', {
28
+ key: 'Escape',
29
+ bubbles: true
30
+ });
31
+ }
32
+ if (reason === 'light-dismiss') {
33
+ return new MouseEvent('click', {
34
+ bubbles: true,
35
+ cancelable: true
36
+ });
37
+ }
38
+ return new Event('close');
39
+ }
@@ -0,0 +1,2 @@
1
+ export { Popover } from './popover';
2
+ export { createPopoverCloseEvent } from './create-close-event';
@@ -0,0 +1,13 @@
1
+
2
+ ._19itglyw{border:none}
3
+ ._1r041wug{inset:auto}._18m9ewfl{overflow-y:visible}
4
+ ._18u0idpf{margin-left:0}
5
+ ._19bvidpf{padding-left:0}
6
+ ._19pkidpf{margin-top:0}
7
+ ._1reoewfl{overflow-x:visible}
8
+ ._2hwxidpf{margin-right:0}
9
+ ._bfhk1j28{background-color:transparent}
10
+ ._ca0qidpf{padding-top:0}
11
+ ._n3tdidpf{padding-bottom:0}
12
+ ._otyridpf{margin-bottom:0}
13
+ ._u5f3idpf{padding-right:0}
@@ -0,0 +1,240 @@
1
+ /* popover.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ import _extends from "@babel/runtime/helpers/extends";
3
+ import "./popover.compiled.css";
4
+ import { ax, ix } from "@compiled/react/runtime";
5
+ import React, { forwardRef, useCallback, useEffect, useId, useLayoutEffect, useRef } from 'react';
6
+ import { bind } from 'bind-event-listener';
7
+ import mergeRefs from '@atlaskit/ds-lib/merge-refs';
8
+ import noop from '@atlaskit/ds-lib/noop';
9
+ import once from '@atlaskit/ds-lib/once';
10
+ import { useAnimatedVisibility } from '../internal/use-animated-visibility';
11
+ import { useFocusWrap } from '../internal/use-focus-wrap';
12
+ import { useInitialFocus } from '../internal/use-initial-focus';
13
+ /**
14
+ * Detects whether the browser supports the `popover="hint"` attribute value.
15
+ * Uses DOM reflection: unsupported browsers reflect the invalid value default ("manual").
16
+ * Safe for SSR. Result is cached via `once`.
17
+ */
18
+ const supportsPopoverHint = once(() => {
19
+ if (typeof document === 'undefined') {
20
+ return false;
21
+ }
22
+ // eslint-disable-next-line @atlaskit/platform/no-direct-document-usage -- feature detection
23
+ const el = document.createElement('div');
24
+ el.setAttribute('popover', 'hint');
25
+ return el.popover === 'hint';
26
+ });
27
+
28
+ // eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values -- popover UA reset requires values not in cssMap's type union
29
+ const styles = {
30
+ root: "_19itglyw _ca0qidpf _u5f3idpf _n3tdidpf _19bvidpf _19pkidpf _2hwxidpf _otyridpf _18u0idpf _1r041wug _1reoewfl _18m9ewfl _bfhk1j28"
31
+ };
32
+ /**
33
+ * Unopinionated top-layer primitive.
34
+ *
35
+ * Manages only visibility (isOpen) and animation (animate). Has no knowledge
36
+ * of positioning — compose with `useAnchorPosition` for anchor positioning.
37
+ *
38
+ * Visibility is driven by the `isOpen` prop:
39
+ * - `isOpen={true}` → `showPopover()`, entry animation via `@starting-style`
40
+ * - `isOpen={false}` → `hidePopover()`, exit animation via `allow-discrete`
41
+ *
42
+ * For `mode="auto"`, the browser can dismiss the popover via light dismiss
43
+ * (Escape, click outside). When that happens, `onClose` is called and the
44
+ * consumer must set `isOpen` to `false`. The DOM owns the dismiss — React
45
+ * must follow.
46
+ */
47
+ export const Popover = /*#__PURE__*/forwardRef(function Popover({
48
+ children,
49
+ mode: modeProp = 'auto',
50
+ onClose = noop,
51
+ onOpenChange,
52
+ onExitFinish,
53
+ animate,
54
+ placement,
55
+ testId,
56
+ isOpen,
57
+ // ARIA
58
+ role,
59
+ label,
60
+ labelledBy,
61
+ id: idProp
62
+ }, ref) {
63
+ const autoId = useId();
64
+ const ownRef = useRef(null);
65
+ const combinedRef = mergeRefs([ownRef, ref].filter(Boolean));
66
+ // Regex is intentional — `replaceAll` is not available in our supported
67
+ // browser matrix. In React 18, `useId()` returns IDs containing colons
68
+ // (e.g. `:r1:`) which are invalid in CSS selectors and `popover` target
69
+ // attributes. React 19.2.0 replaced colons with underscores, making this
70
+ // stripping unnecessary once React 18 support is dropped.
71
+ const popoverId = idProp !== null && idProp !== void 0 ? idProp : `popover-${autoId.replace(/:/g, '')}`;
72
+ const {
73
+ showChildren,
74
+ preset
75
+ } = useAnimatedVisibility({
76
+ isOpen,
77
+ animate,
78
+ elementRef: ownRef,
79
+ onExitFinish
80
+ });
81
+
82
+ // ── Focus management ──
83
+ // Initial focus: on open, moves focus into the popover (role-dependent).
84
+ // Focus wrap: Tab/Shift+Tab cycle within the popover for dialog roles.
85
+ // Focus restoration: handled natively by the Popover API — the browser
86
+ // tracks `previouslyFocusedElement` on show and restores it on hide.
87
+ // No custom restoration hook is needed. See: notes/architecture/focus-restoration.md
88
+ useFocusWrap({
89
+ elementRef: ownRef,
90
+ role
91
+ });
92
+ useInitialFocus({
93
+ elementRef: ownRef,
94
+ isOpen,
95
+ role
96
+ });
97
+
98
+ // Hint mode fallback: "hint" downgrades to "auto" if unsupported.
99
+ //
100
+ // Decision (2026-03-17 audit): We keep "auto" as the fallback (not "manual")
101
+ // because it provides the closest behavior to "hint" (light dismiss works).
102
+ // However, there is a behavioral difference: "auto" participates in the
103
+ // auto-dismiss stack and WILL close other "auto" popovers when it opens
104
+ // (e.g. a tooltip opening will close an open dropdown). "hint" does not
105
+ // have this side effect. This difference only affects browsers without
106
+ // popover="hint" support, and that set is shrinking rapidly.
107
+ const mode = modeProp === 'hint' && !supportsPopoverHint() ? 'auto' : modeProp;
108
+
109
+ // Distinguishes programmatic hidePopover() from browser-initiated dismiss.
110
+ // When we call hidePopover() ourselves the toggle event fires synchronously —
111
+ // this ref prevents the toggle handler from calling onClose redundantly.
112
+ const programmaticCloseRef = useRef(false);
113
+
114
+ // Escape key tracking: set in a capture-phase keydown listener (fires before
115
+ // the browser processes light dismiss), read in the toggle handler.
116
+ const closeReasonRef = useRef('light-dismiss');
117
+ const handleToggle = useCallback(event => {
118
+ const el = ownRef.current;
119
+ if (event.newState === 'open') {
120
+ if (el) {
121
+ onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange({
122
+ isOpen: true,
123
+ element: el
124
+ });
125
+ }
126
+ return;
127
+ }
128
+ if (event.newState === 'closed') {
129
+ if (el) {
130
+ onOpenChange === null || onOpenChange === void 0 ? void 0 : onOpenChange({
131
+ isOpen: false,
132
+ element: el
133
+ });
134
+ }
135
+
136
+ // Programmatic close (unmount or isOpen→false). Consumer already knows.
137
+ if (programmaticCloseRef.current) {
138
+ return;
139
+ }
140
+
141
+ // Browser dismiss (Escape or click-outside). Notify consumer.
142
+ const reason = closeReasonRef.current;
143
+ closeReasonRef.current = 'light-dismiss'; // reset for next dismiss
144
+ onClose({
145
+ reason
146
+ });
147
+ }
148
+ }, [onClose, onOpenChange]);
149
+ useEffect(() => {
150
+ const el = ownRef.current;
151
+ if (!el) {
152
+ return;
153
+ }
154
+
155
+ // Capture-phase: tag Escape before the browser processes light dismiss.
156
+ const unbindEscape = bind(el, {
157
+ type: 'keydown',
158
+ listener: event => {
159
+ if (event.key === 'Escape') {
160
+ closeReasonRef.current = 'escape';
161
+ }
162
+ },
163
+ options: {
164
+ capture: true
165
+ }
166
+ });
167
+ const unbindToggle = bind(el, {
168
+ type: 'toggle',
169
+ listener: handleToggle
170
+ });
171
+ return () => {
172
+ unbindEscape();
173
+ unbindToggle();
174
+ };
175
+ }, [handleToggle]);
176
+
177
+ // Placement-dependent animation CSS vars (e.g. slide direction).
178
+ // Separate from show/hide so that preset reference changes
179
+ // (e.g. inline `animate={slideAndFade()}`) don't re-trigger visibility.
180
+ useLayoutEffect(() => {
181
+ const el = ownRef.current;
182
+ if (!el || !(preset !== null && preset !== void 0 && preset.getProperties) || !placement) {
183
+ return;
184
+ }
185
+ const props = preset.getProperties({
186
+ placement
187
+ });
188
+ Object.entries(props).forEach(([key, value]) => {
189
+ el.style.setProperty(key, String(value));
190
+ });
191
+ }, [preset, placement]);
192
+
193
+ // Show/hide based on isOpen. try/catch because show/hide throw if the
194
+ // element is already in the target state (e.g. browser dismissed via
195
+ // light-dismiss before our effect ran).
196
+ useLayoutEffect(() => {
197
+ const el = ownRef.current;
198
+ if (!el) {
199
+ return;
200
+ }
201
+ if (isOpen) {
202
+ programmaticCloseRef.current = false;
203
+ try {
204
+ el.showPopover();
205
+ } catch {
206
+ // Already showing or unsupported — DOM matches intent.
207
+ }
208
+ return () => {
209
+ programmaticCloseRef.current = true;
210
+ try {
211
+ el.hidePopover();
212
+ } catch {
213
+ // Already hidden — DOM matches intent.
214
+ }
215
+ };
216
+ }
217
+ programmaticCloseRef.current = true;
218
+ try {
219
+ el.hidePopover();
220
+ } catch {
221
+ // Already hidden — DOM matches intent.
222
+ }
223
+ }, [isOpen]);
224
+ return /*#__PURE__*/React.createElement("div", _extends({
225
+ ref: combinedRef,
226
+ id: popoverId
227
+ // @ts-expect-error -- popover attribute not yet in React types
228
+ // eslint-disable-next-line react/no-unknown-property -- popover attribute not yet in React types
229
+ ,
230
+ popover: mode,
231
+ role: role,
232
+ "aria-label": label,
233
+ "aria-labelledby": labelledBy,
234
+ "data-testid": testId
235
+ }, preset ? {
236
+ [`data-ds-popover-${preset.name}`]: ''
237
+ } : undefined, {
238
+ className: ax([styles.root])
239
+ }), showChildren ? children : null);
240
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
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 };