@atlaskit/top-layer 0.9.0 → 0.11.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 (127) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +5 -1
  3. package/dialog/create-close-event/package.json +17 -0
  4. package/dist/cjs/animations/presets.js +10 -5
  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 +11 -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 +71 -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 +107 -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 -111
  20. package/dist/cjs/placement-map/legacy-placements.js +113 -0
  21. package/dist/cjs/popover/popover.js +14 -12
  22. package/dist/cjs/popup/popup-content.js +19 -13
  23. package/dist/cjs/popup/popup-surface.js +6 -0
  24. package/dist/cjs/popup/popup-trigger-function.js +1 -1
  25. package/dist/cjs/popup/popup-trigger.js +17 -37
  26. package/dist/cjs/popup/popup.js +21 -10
  27. package/dist/cjs/use-arrow-navigation/use-arrow-navigation.js +26 -10
  28. package/dist/es2019/animations/presets.js +20 -5
  29. package/dist/es2019/arrow/index.js +269 -0
  30. package/dist/es2019/arrow/types.js +1 -0
  31. package/dist/es2019/dialog/dialog-content.js +11 -6
  32. package/dist/es2019/entry-points/create-close-event.js +7 -0
  33. package/dist/es2019/entry-points/dialog/create-close-event.js +1 -0
  34. package/dist/es2019/entry-points/placement-map.js +2 -1
  35. package/dist/es2019/entry-points/popover/create-close-event.js +1 -0
  36. package/dist/es2019/focus/focus.js +56 -7
  37. package/dist/es2019/index.js +3 -4
  38. package/dist/es2019/internal/resolve-css-length-to-pixels.js +60 -7
  39. package/dist/es2019/internal/role-types.js +40 -9
  40. package/dist/es2019/internal/set-style.js +19 -22
  41. package/dist/es2019/internal/use-anchor-position.js +95 -54
  42. package/dist/es2019/internal/use-focus-wrap.js +14 -3
  43. package/dist/es2019/internal/use-initial-focus.js +32 -11
  44. package/dist/es2019/placement-map/index.js +8 -109
  45. package/dist/es2019/placement-map/legacy-placements.js +107 -0
  46. package/dist/es2019/popover/popover.js +14 -11
  47. package/dist/es2019/popup/popup-content.js +20 -14
  48. package/dist/es2019/popup/popup-surface.js +6 -0
  49. package/dist/es2019/popup/popup-trigger-function.js +1 -1
  50. package/dist/es2019/popup/popup-trigger.js +17 -37
  51. package/dist/es2019/popup/popup.js +20 -9
  52. package/dist/es2019/use-arrow-navigation/use-arrow-navigation.js +26 -10
  53. package/dist/esm/animations/presets.js +9 -4
  54. package/dist/esm/arrow/index.js +124 -0
  55. package/dist/esm/arrow/types.js +1 -0
  56. package/dist/esm/dialog/dialog-content.js +11 -6
  57. package/dist/esm/entry-points/create-close-event.js +7 -0
  58. package/dist/esm/entry-points/dialog/create-close-event.js +1 -0
  59. package/dist/esm/entry-points/placement-map.js +2 -1
  60. package/dist/esm/entry-points/popover/create-close-event.js +1 -0
  61. package/dist/esm/focus/focus.js +70 -19
  62. package/dist/esm/index.js +3 -4
  63. package/dist/esm/internal/resolve-css-length-to-pixels.js +61 -10
  64. package/dist/esm/internal/role-types.js +39 -9
  65. package/dist/esm/internal/set-style.js +23 -25
  66. package/dist/esm/internal/use-anchor-position.js +108 -64
  67. package/dist/esm/internal/use-focus-wrap.js +14 -3
  68. package/dist/esm/internal/use-initial-focus.js +32 -11
  69. package/dist/esm/placement-map/index.js +8 -109
  70. package/dist/esm/placement-map/legacy-placements.js +107 -0
  71. package/dist/esm/popover/popover.js +14 -12
  72. package/dist/esm/popup/popup-content.js +20 -14
  73. package/dist/esm/popup/popup-surface.js +6 -0
  74. package/dist/esm/popup/popup-trigger-function.js +1 -1
  75. package/dist/esm/popup/popup-trigger.js +17 -37
  76. package/dist/esm/popup/popup.js +21 -10
  77. package/dist/esm/use-arrow-navigation/use-arrow-navigation.js +26 -10
  78. package/dist/types/arrow/index.d.ts +24 -0
  79. package/dist/types/arrow/types.d.ts +29 -0
  80. package/dist/types/dialog/dialog-content.d.ts +5 -1
  81. package/dist/types/dialog/types.d.ts +50 -24
  82. package/dist/types/entry-points/create-close-event.d.ts +7 -0
  83. package/dist/types/entry-points/dialog/create-close-event.d.ts +2 -0
  84. package/dist/types/entry-points/placement-map.d.ts +2 -1
  85. package/dist/types/entry-points/popover/create-close-event.d.ts +2 -0
  86. package/dist/types/focus/focus.d.ts +8 -0
  87. package/dist/types/internal/resolve-css-length-to-pixels.d.ts +4 -6
  88. package/dist/types/internal/role-types.d.ts +25 -4
  89. package/dist/types/internal/set-style.d.ts +2 -19
  90. package/dist/types/internal/use-anchor-position.d.ts +2 -3
  91. package/dist/types/internal/use-initial-focus.d.ts +5 -4
  92. package/dist/types/placement-map/index.d.ts +8 -21
  93. package/dist/types/placement-map/legacy-placements.d.ts +21 -0
  94. package/dist/types/popover/popover.d.ts +1 -1
  95. package/dist/types/popover/types.d.ts +17 -17
  96. package/dist/types/popup/popup-surface.d.ts +6 -0
  97. package/dist/types/popup/popup-trigger-function.d.ts +10 -6
  98. package/dist/types/popup/popup-trigger.d.ts +8 -17
  99. package/dist/types/popup/popup.d.ts +2 -2
  100. package/dist/types/popup/types.d.ts +68 -22
  101. package/dist/types/use-arrow-navigation/use-arrow-navigation.d.ts +19 -5
  102. package/dist/types-ts4.5/arrow/index.d.ts +24 -0
  103. package/dist/types-ts4.5/arrow/types.d.ts +29 -0
  104. package/dist/types-ts4.5/dialog/dialog-content.d.ts +5 -1
  105. package/dist/types-ts4.5/dialog/types.d.ts +50 -24
  106. package/dist/types-ts4.5/entry-points/create-close-event.d.ts +7 -0
  107. package/dist/types-ts4.5/entry-points/dialog/create-close-event.d.ts +2 -0
  108. package/dist/types-ts4.5/entry-points/placement-map.d.ts +2 -1
  109. package/dist/types-ts4.5/entry-points/popover/create-close-event.d.ts +2 -0
  110. package/dist/types-ts4.5/focus/focus.d.ts +8 -0
  111. package/dist/types-ts4.5/internal/resolve-css-length-to-pixels.d.ts +4 -6
  112. package/dist/types-ts4.5/internal/role-types.d.ts +25 -4
  113. package/dist/types-ts4.5/internal/set-style.d.ts +2 -19
  114. package/dist/types-ts4.5/internal/use-anchor-position.d.ts +2 -3
  115. package/dist/types-ts4.5/internal/use-initial-focus.d.ts +5 -4
  116. package/dist/types-ts4.5/placement-map/index.d.ts +8 -21
  117. package/dist/types-ts4.5/placement-map/legacy-placements.d.ts +39 -0
  118. package/dist/types-ts4.5/popover/popover.d.ts +1 -1
  119. package/dist/types-ts4.5/popover/types.d.ts +17 -17
  120. package/dist/types-ts4.5/popup/popup-surface.d.ts +6 -0
  121. package/dist/types-ts4.5/popup/popup-trigger-function.d.ts +10 -6
  122. package/dist/types-ts4.5/popup/popup-trigger.d.ts +8 -17
  123. package/dist/types-ts4.5/popup/popup.d.ts +2 -2
  124. package/dist/types-ts4.5/popup/types.d.ts +68 -22
  125. package/dist/types-ts4.5/use-arrow-navigation/use-arrow-navigation.d.ts +19 -5
  126. package/package.json +1 -1
  127. package/popover/create-close-event/package.json +17 -0
@@ -1,7 +1,7 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import { type StrictXCSSProp } from '@atlaskit/css';
3
3
  import { type TAnimationPreset } from '../animations/types';
4
- import { type TAriaRoleOptional, type TRoleRequiringAccessibleName, type TRoleWithImplicitName } from '../internal/role-types';
4
+ import { type TAriaRoleRequired, type TRoleRequiringAccessibleName, type TRoleWithImplicitName } from '../internal/role-types';
5
5
  import { type TPlacementOptions } from '../popup/types';
6
6
  /**
7
7
  * The reason a popover was closed.
@@ -31,13 +31,6 @@ export type TPopoverForwardedProps = TPopoverBaseProps & {
31
31
  label?: string;
32
32
  labelledBy?: string;
33
33
  };
34
- /**
35
- * Alias for TPopoverForwardedProps.
36
- *
37
- * Used internally by PopupContent to forward props to Popover without
38
- * re-proving the discriminated union at each call site.
39
- */
40
- export type TPopoverInternalProps = TPopoverForwardedProps;
41
34
  type TPopoverBaseProps = {
42
35
  children: ReactNode;
43
36
  /**
@@ -70,12 +63,12 @@ type TPopoverBaseProps = {
70
63
  * animation plays (via `allow-discrete`) before the popover becomes logically closed.
71
64
  * Otherwise hides instantly.
72
65
  *
73
- * The element stays mounted in the DOM at all times — the consumer does not
66
+ * The element stays mounted in the DOM at all times. The consumer does not
74
67
  * conditionally render it. Visibility is driven by this prop.
75
68
  *
76
69
  * **Important:** For `mode="auto"` popovers, the browser can dismiss the popover
77
70
  * via light dismiss (Escape, click outside) independently of this prop. When that
78
- * happens, `onClose` is called — the consumer should respond by setting `isOpen`
71
+ * happens, `onClose` is called and the consumer should respond by setting `isOpen`
79
72
  * to `false`. If `isOpen` remains `true` after a browser dismiss, the DOM and
80
73
  * React state will be out of sync (the popover will be hidden despite `isOpen={true}`).
81
74
  */
@@ -109,14 +102,14 @@ type TPopoverBaseProps = {
109
102
  * called to set CSS custom properties (like `--ds-popover-tx`, `--ds-popover-ty`)
110
103
  * that control the slide direction. Has no effect without `animate`.
111
104
  *
112
- * This does NOT control positioning — use `useAnchorPosition` for that.
105
+ * This does NOT control positioning. Use `useAnchorPosition` for that.
113
106
  */
114
107
  placement?: TPlacementOptions;
115
108
  /**
116
109
  * Called after the exit animation completes (or immediately on close when
117
110
  * there is no animation or reduced motion is active).
118
111
  *
119
- * Use this for external lifecycle coordination — e.g. notifying a manager
112
+ * Use this for external lifecycle coordination, e.g. notifying a manager
120
113
  * that the hide sequence is finished, or firing an `onCloseComplete` callback.
121
114
  */
122
115
  onExitFinish?: () => void;
@@ -124,7 +117,7 @@ type TPopoverBaseProps = {
124
117
  /**
125
118
  * Unopinionated top-layer primitive.
126
119
  *
127
- * `Popover` manages only top-layer visibility and animation — it has no
120
+ * `Popover` manages only top-layer visibility and animation. It has no
128
121
  * knowledge of positioning. For anchor positioning, compose with the
129
122
  * `useAnchorPosition` hook separately.
130
123
  *
@@ -135,22 +128,29 @@ type TPopoverBaseProps = {
135
128
  * Dismiss behavior:
136
129
  * - `'auto'` (default): Escape to close, click-outside to close, focus restoration,
137
130
  * automatic nesting, mutual exclusivity.
138
- * - `'hint'`: ephemeral UI (e.g. tooltips) that doesn't close other `auto` popovers.
139
- * Falls back to `'auto'` in browsers without `popover="hint"` support.
131
+ * - `'hint'`: ephemeral UI (e.g. tooltips) that does not close other `auto`
132
+ * popovers. Falls back silently to `auto` semantics in browsers without
133
+ * `popover='hint'` support, meaning the hint will close any other `auto`
134
+ * popovers in those browsers. If cross-browser uniformity matters more
135
+ * than the no-close behaviour, pass `auto` explicitly.
140
136
  * - `'manual'`: no light dismiss. Consumer controls show/hide entirely. Only
141
137
  * appropriate for persistent UI (e.g. flags, banners).
142
138
  */
143
- export type TPopoverProps = TPopoverBaseProps & TAriaRoleOptional & ({
139
+ export type TPopoverProps = TPopoverBaseProps & TAriaRoleRequired & ({
144
140
  mode?: 'auto' | 'hint';
145
141
  /**
146
142
  * Called when the popover is dismissed via light dismiss
147
143
  * (Escape, click outside).
148
144
  *
145
+ * Required for `auto` and `hint` modes - without it, browser
146
+ * dismissals leave the consumer's `isOpen` stuck at `true` while
147
+ * the DOM is hidden, producing a stale-open state.
148
+ *
149
149
  * The `reason` field indicates how the dismiss occurred:
150
150
  * - `'escape'`: the user pressed the Escape key.
151
151
  * - `'light-dismiss'`: the user clicked outside (or another auto-dismiss).
152
152
  */
153
- onClose?: (args: {
153
+ onClose: (args: {
154
154
  reason: TPopoverCloseReason;
155
155
  }) => void;
156
156
  } | {
@@ -12,5 +12,11 @@ import { type TPopupSurfaceProps } from './types';
12
12
  * should use `Popup.Content` without wrapping in `PopupSurface`.
13
13
  *
14
14
  * Exposed as `Popup.Surface` on the compound component.
15
+ *
16
+ * **Presentational primitive - `children`-only by design.** This component
17
+ * intentionally exposes no `style`, `className`, `xcss`, `id`, or `ref`
18
+ * props. If you need any of those, render your own wrapper element inside
19
+ * `Popup.Content` instead - `PopupSurface` is the no-config "give me the
20
+ * default ADS overlay look" surface, nothing more.
15
21
  */
16
22
  export declare function PopupSurface({ children }: TPopupSurfaceProps): React.ReactElement;
@@ -38,10 +38,14 @@ export type TTriggerFunctionRenderProps = {
38
38
  *
39
39
  * **Note on `aria-haspopup`:** When the content's role maps to a specific
40
40
  * popup type (`'menu'`, `'dialog'`, `'listbox'`, etc.), the corresponding
41
- * string value is used. For unmapped roles, the value is `true` (boolean).
42
- * React serialises `aria-haspopup={true}` as `aria-haspopup="true"` in the
43
- * DOM, which is the correct default per the HTML spec. The values can be
44
- * spread directly onto trigger elements without conversion.
41
+ * string value is emitted. For non-popup roles (`tooltip`, `note`,
42
+ * `status`, `alert`, `log`) the value is `undefined`, so React drops the
43
+ * attribute entirely - these triggers should not announce a popup.
44
+ *
45
+ * The value type (`TAriaHasPopupValue`) is internal and may narrow further
46
+ * in future minor versions. Do **not** extract it via `Parameters<…>`.
47
+ * Spread `ariaAttributes` directly onto the trigger; do not destructure
48
+ * and re-type.
45
49
  */
46
50
  ariaAttributes: {
47
51
  'aria-expanded': boolean;
@@ -53,7 +57,7 @@ type TPopupTriggerFunctionProps = {
53
57
  /**
54
58
  * Render-prop function that receives trigger props.
55
59
  *
56
- * Unlike `Popup.Trigger`, this does not use `cloneElement` — the consumer
60
+ * Unlike `Popup.Trigger`, this does not use `cloneElement`. The consumer
57
61
  * is responsible for spreading ref, ARIA attributes, and attaching `toggle`
58
62
  * to the appropriate event(s).
59
63
  *
@@ -77,7 +81,7 @@ type TPopupTriggerFunctionProps = {
77
81
  /**
78
82
  * Render-prop alternative to `Popup.Trigger`.
79
83
  *
80
- * Use when you need full control over the trigger element — for example,
84
+ * Use when you need full control over the trigger element. For example,
81
85
  * to compose your own trigger with custom event handlers, wrap a third-party
82
86
  * component, or build a trigger from multiple elements.
83
87
  *
@@ -1,23 +1,14 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import { type TPopupTriggerProps } from './types';
3
3
  /**
4
- * Wraps the trigger element. Attaches aria attributes and handles
5
- * click to toggle the popover. Sets the anchor-name on the trigger
6
- * for CSS Anchor Positioning.
4
+ * Wraps a single trigger element via `cloneElement`. Attaches aria
5
+ * attributes, the toggle handler, and the anchor-name. `aria-haspopup`
6
+ * is derived from `Popup.Content`'s role via context.
7
7
  *
8
- * `aria-haspopup` is derived from the content's role via context
9
- * (set by `Popup.Content`). This ensures the trigger always matches
10
- * the content's semantic role without the consumer specifying it
11
- * in two places.
12
- *
13
- * **Limitation:** Uses `cloneElement` internally (via `Slot`), which is
14
- * incompatible with Compiled's `css` prop on the direct child element.
15
- * The `@jsx jsx` pragma transforms the child before `cloneElement` can
16
- * inject `onClick` and `ref` onto the DOM node, preventing the popover
17
- * from opening.
18
- *
19
- * If you need Compiled `css` on the trigger, use `Popup.TriggerFunction`
20
- * instead - it hands you raw props via a render-prop and avoids
21
- * `cloneElement` entirely.
8
+ * Child constraints: must be a single ref-forwarding element. The child's
9
+ * `onClick` is composed (runs after); other event handlers are NOT.
10
+ * Children using Compiled's `css` prop swallow injected props - use
11
+ * `Popup.TriggerFunction` for these cases or any time you need handler
12
+ * composition.
22
13
  */
23
14
  export declare function PopupTrigger({ children }: TPopupTriggerProps): ReactNode;
@@ -1,10 +1,10 @@
1
1
  import React from 'react';
2
2
  import { type TPopupProps } from './types';
3
3
  /**
4
- * Popup compound — trigger + popover content.
4
+ * Popup compound: trigger + popover content.
5
5
  *
6
6
  * Use when a button opens anchored content (dropdown, menu, dialog).
7
- * The browser manages visibility via `togglePopover()` — the consumer
7
+ * The browser manages visibility via `togglePopover()`; the consumer
8
8
  * never manages `isOpen`. For custom trigger lifecycles (hover, timers,
9
9
  * external state), use `Popover` directly instead.
10
10
  *
@@ -25,26 +25,27 @@ import { type TPopoverCloseReason, type TWidthFromAnchorMode } from '../popover/
25
25
  * ```
26
26
  */
27
27
  export { type TPlacementOptions } from '../internal/resolve-placement';
28
- export type TPopupProps = {
28
+ /**
29
+ * Props shared across all `Popup` modes.
30
+ *
31
+ * Mode-specific behaviour for `onClose` is encoded in the discriminated
32
+ * union below: `'auto'` and `'hint'` modes accept an `onClose` handler
33
+ * (called on light dismiss); `'manual'` mode forbids it (no light dismiss
34
+ * exists, so a handler would be dead code).
35
+ */
36
+ type TPopupBaseProps = {
29
37
  /**
30
38
  * Position of the popup relative to the trigger. Only the Popup compound uses
31
39
  * placement; the low-level Popover primitive does not.
40
+ *
41
+ * Defaults to `{}` which means "below the trigger, centered, with a
42
+ * `space.100` token gap".
32
43
  */
33
- placement: TPlacementOptions;
44
+ placement?: TPlacementOptions;
34
45
  /**
35
46
  * Must contain `Popup.Trigger` and `Popup.Content`.
36
47
  */
37
48
  children: ReactNode;
38
- /**
39
- * Called when the popup is dismissed via light dismiss.
40
- *
41
- * The `reason` field indicates how the dismiss occurred:
42
- * - `'escape'`: the user pressed the Escape key.
43
- * - `'light-dismiss'`: the user clicked outside (or another auto-dismiss).
44
- */
45
- onClose: (args: {
46
- reason: TPopoverCloseReason;
47
- }) => void;
48
49
  /**
49
50
  * Called when the popup's open state changes.
50
51
  *
@@ -67,14 +68,6 @@ export type TPopupProps = {
67
68
  isOpen: boolean;
68
69
  element: HTMLDivElement;
69
70
  }) => void;
70
- /**
71
- * Native popover attribute value. Default is `'auto'`.
72
- *
73
- * - `'auto'`: light dismiss (Escape, click-outside). Only one `popover="auto"` can be open at a time; opening another closes the previous.
74
- * - `'hint'`: allows ephemeral UI (e.g. tooltips) to open without closing other `popover="auto"` popovers. Falls back to `popover="auto"` when unsupported.
75
- * - `'manual'`: no light dismiss; the consumer controls show/hide entirely. Escape and click-outside do not close the popover.
76
- */
77
- mode?: 'auto' | 'hint' | 'manual';
78
71
  /**
79
72
  * Test ID applied to the popup content element.
80
73
  */
@@ -86,6 +79,49 @@ export type TPopupProps = {
86
79
  */
87
80
  forceFallbackPositioning?: boolean;
88
81
  };
82
+ /**
83
+ * Props for the `Popup` compound.
84
+ *
85
+ * Discriminated on `mode`:
86
+ * - `'auto'` (default) and `'hint'`: light dismiss is enabled; `onClose` is
87
+ * optional and is called when the browser dismisses the popover.
88
+ * - `'manual'`: no light dismiss; `onClose` is `never` since the consumer
89
+ * owns show/hide entirely and there is no dismiss event to handle.
90
+ */
91
+ export type TPopupProps = TPopupBaseProps & ({
92
+ /**
93
+ * Native popover attribute value. Default is `'auto'`.
94
+ *
95
+ * - `'auto'`: light dismiss (Escape, click-outside). Only one
96
+ * `popover="auto"` can be open at a time; opening another closes
97
+ * the previous.
98
+ * - `'hint'`: allows ephemeral UI (e.g. tooltips) to open without
99
+ * closing other `popover="auto"` popovers. Falls back to
100
+ * `popover="auto"` when unsupported.
101
+ */
102
+ mode?: 'auto' | 'hint';
103
+ /**
104
+ * Called when the popup is dismissed via light dismiss.
105
+ *
106
+ * The `reason` field indicates how the dismiss occurred:
107
+ * - `'escape'`: the user pressed the Escape key.
108
+ * - `'light-dismiss'`: the user clicked outside (or another auto-dismiss).
109
+ */
110
+ onClose?: (args: {
111
+ reason: TPopoverCloseReason;
112
+ }) => void;
113
+ } | {
114
+ /**
115
+ * `'manual'`: no light dismiss; the consumer controls show/hide
116
+ * entirely. Escape and click-outside do not close the popover.
117
+ */
118
+ mode: 'manual';
119
+ /**
120
+ * Manual mode has no dismiss event, so `onClose` is forbidden.
121
+ * The consumer owns show/hide via their own state.
122
+ */
123
+ onClose?: never;
124
+ });
89
125
  export type TPopupTriggerProps = {
90
126
  /**
91
127
  * The trigger element. Must accept a ref.
@@ -105,7 +141,7 @@ export type TPopupRole = TRoleRequiringAccessibleName | TRoleWithImplicitName;
105
141
  *
106
142
  * When used standalone (e.g. tooltip, spotlight), the consumer must provide `isOpen`
107
143
  * to control visibility. If `animate` is provided, `isOpen` is required at the type
108
- * level — this ensures exit animations always have a controlled lifecycle.
144
+ * level. This ensures exit animations always have a controlled lifecycle.
109
145
  */
110
146
  type TPopupContentBaseProps = {
111
147
  children: ReactNode;
@@ -113,6 +149,16 @@ type TPopupContentBaseProps = {
113
149
  * Ref to the underlying `<div popover>` element.
114
150
  */
115
151
  ref?: Ref<HTMLDivElement>;
152
+ /**
153
+ * HTML `id` attribute for the popover element.
154
+ *
155
+ * Provide this when using `Popup.Content` standalone (outside the
156
+ * `<Popup>` compound). The consumer is responsible for wiring
157
+ * `aria-controls` on their own trigger to this same id. Inside the
158
+ * `<Popup>` compound, the id flows from context - passing it here is
159
+ * unnecessary.
160
+ */
161
+ id?: string;
116
162
  /**
117
163
  * Controls the width of the popover relative to its anchor element.
118
164
  *
@@ -197,7 +243,7 @@ type TPopupContentBaseProps = {
197
243
  * Inside the `<Popup>` compound, `isOpen` is provided automatically via context
198
244
  * (the browser manages state through `togglePopover()`).
199
245
  *
200
- * For standalone usage, provide `isOpen` to control visibility — especially
246
+ * For standalone usage, provide `isOpen` to control visibility, especially
201
247
  * when `animate` is set, so the exit animation lifecycle works correctly.
202
248
  *
203
249
  * @example
@@ -2,16 +2,16 @@ import { type RefObject } from 'react';
2
2
  import { type TFocusableFilter } from '../focus';
3
3
  /**
4
4
  * Returns true if the element belongs to the current menu level of the
5
- * container — i.e. it is NOT inside a nested `role="menu"` sub-container.
5
+ * container, i.e. it is NOT inside a nested `role="menu"` sub-container.
6
6
  *
7
7
  * Uses `element.closest('[role="menu"]')` to determine the nearest menu
8
8
  * scope. If the nearest scope is the container's own `role="menu"` element,
9
- * the element belongs to this level. If it's a different (nested) menu,
9
+ * the element belongs to this level. If it is a different (nested) menu,
10
10
  * the element belongs to a child level and should be excluded.
11
11
  *
12
12
  * Supports two container patterns:
13
13
  *
14
- * **1. Container has `role="menu"` directly** (preferred — used by avatar-group):
14
+ * **1. Container has `role="menu"` directly** (preferred, used by avatar-group):
15
15
  * ```
16
16
  * <div ref={containerRef} role="menu">
17
17
  * <button role="menuitem" /> ← isAtCurrentMenuLevel → true
@@ -23,7 +23,7 @@ import { type TFocusableFilter } from '../focus';
23
23
  * Uses the simple check: `element.closest('[role="menu"]') === container`
24
24
  *
25
25
  * **2. Container is a wrapper without `role="menu"`** (used by dropdown-menu,
26
- * where `MenuGroup` owns `role="menu"` but doesn't accept a ref):
26
+ * where `MenuGroup` owns `role="menu"` but does not accept a ref):
27
27
  * ```
28
28
  * <div ref={containerRef}> ← wrapper (no role="menu")
29
29
  * <MenuGroup role="menu">
@@ -48,6 +48,20 @@ import { type TFocusableFilter } from '../focus';
48
48
  * belong to a nested menu.
49
49
  */
50
50
  export declare function isAtCurrentMenuLevel(element: Element, container: HTMLElement): boolean;
51
+ /**
52
+ * Args for `useArrowNavigation`.
53
+ *
54
+ * Keyboard contract:
55
+ * - `ArrowDown` / `ArrowUp` move within the current menu level.
56
+ * - `Home` / `End` jump to first / last.
57
+ * - `ArrowRight` / `ArrowLeft` opens / closes nested submenus.
58
+ * - `Tab` / `Shift+Tab` dismiss the menu (matching the ARIA APG menu pattern,
59
+ * plus Radix UI / Headless UI / React Aria / Floating UI conventions).
60
+ * Symmetric in both directions: there is no special-case for the last
61
+ * item that lets Tab leave the menu without dismissing it.
62
+ * - `Page Up` / `Page Down`: NOT handled - extend via your own listener if
63
+ * your menu needs page-style scrolling.
64
+ */
51
65
  export type TUseArrowNavigationArgs = {
52
66
  /**
53
67
  * Ref to the container element that holds the focusable items.
@@ -89,7 +103,7 @@ export type TUseArrowNavigationArgs = {
89
103
  * When provided, only elements that pass the filter will be focused.
90
104
  *
91
105
  * The filter is also used as a guard on the currently focused element:
92
- * if the focused element doesn't pass the filter, the handler bails out.
106
+ * if the focused element does not pass the filter, the handler bails out.
93
107
  * This prevents parent menu handlers from acting on events that belong
94
108
  * to a nested menu.
95
109
  *
@@ -0,0 +1,24 @@
1
+ import { type TArrowPreset } from './types';
2
+ /**
3
+ * Creates an arrow preset for popover content.
4
+ *
5
+ * The returned object is passed to `PopoverContent`'s `arrow` prop.
6
+ * It carries the CSS string and fallback logic so that consumers who
7
+ * do not use arrows pay no bundle cost.
8
+ *
9
+ * The arrows inherit their background from the popover element.
10
+ * When `arrow` is active, `PopoverContent` does not reset `background`
11
+ * to `transparent`, allowing the UA default (`canvas`) or a consumer-set
12
+ * background to show through.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * import { arrow } from '@atlaskit/top-layer/arrow';
17
+ *
18
+ * const myArrow = arrow();
19
+ *
20
+ * <Popup.Content arrow={myArrow} />
21
+ * ```
22
+ */
23
+ export declare const arrow: () => TArrowPreset;
24
+ export type { TArrowPreset } from './types';
@@ -0,0 +1,29 @@
1
+ import type { TPlacementOptions } from '../popup/types';
2
+ /**
3
+ * A self-contained arrow preset for PopoverContent.
4
+ *
5
+ * Contains the CSS rules (injected once into `<head>`), a data-attribute name
6
+ * for CSS targeting, and a function to compute `position-try-fallbacks` values
7
+ * that change both `position-area` and `margin` when the popover flips.
8
+ *
9
+ * Imported from `@atlaskit/top-layer/arrow` so that consumers who do not use
10
+ * arrows pay no bundle cost for the CSS string or fallback logic.
11
+ */
12
+ export type TArrowPreset = {
13
+ /**
14
+ * CSS to inject once into `document.head`, scoped to `[data-ds-popover-{name}]`.
15
+ */
16
+ css: string;
17
+ /**
18
+ * Data attribute suffix for CSS targeting (e.g. `'arrow'`).
19
+ */
20
+ name: string;
21
+ /**
22
+ * Returns the `position-try-fallbacks` value for a given placement.
23
+ * Uses named `@position-try` rules that update both `position-area`
24
+ * and `margin` so the correct arrow is revealed after a flip.
25
+ */
26
+ getTryFallbacks: (args: {
27
+ placement: TPlacementOptions;
28
+ }) => string;
29
+ };
@@ -5,7 +5,7 @@
5
5
  import React from 'react';
6
6
  import { type TDialogProps } from './types';
7
7
  /**
8
- * Low-level `<dialog>` primitive. No visual opinions — no width, height,
8
+ * Low-level `<dialog>` primitive. No visual opinions - no width, height,
9
9
  * background, border-radius, or layout. Consumers provide their own styling.
10
10
  *
11
11
  * Visibility is controlled declaratively via `isOpen`:
@@ -16,5 +16,9 @@ import { type TDialogProps } from './types';
16
16
  *
17
17
  * Close flow: we never call `dialog.close()` from event handlers. We always call
18
18
  * `onClose`; the consumer decides whether to set `isOpen={false}`.
19
+ *
20
+ * Accessibility: render at least one focusable element (typically a close
21
+ * button) when `isOpen` becomes `true`. Tab is always trapped inside the
22
+ * dialog, so keyboard users need somewhere to land.
19
23
  */
20
24
  export declare const Dialog: React.ForwardRefExoticComponent<TDialogProps & React.RefAttributes<HTMLDialogElement>>;
@@ -3,23 +3,21 @@ import { type TAnimationPreset } from '../animations/types';
3
3
  /**
4
4
  * Close reasons produced by the Dialog primitive.
5
5
  *
6
- * - `'escape'` — user pressed Escape (native `cancel` event on `<dialog>`).
7
- * - `'overlay-click'` — user clicked the backdrop / overlay area.
6
+ * - `'escape'`: user pressed Escape (native `cancel` event on `<dialog>`).
7
+ * - `'overlay-click'`: user clicked the backdrop / overlay area.
8
8
  *
9
9
  * Consumer-initiated ("programmatic") close is not a reason the primitive
10
- * produces — the consumer simply sets `isOpen={false}`.
10
+ * produces. The consumer simply sets `isOpen={false}`.
11
11
  */
12
12
  export type TDialogCloseReason = 'escape' | 'overlay-click';
13
13
  /**
14
- * Props for the `Dialog` primitive.
14
+ * Props shared across `TDialogProps` variants.
15
15
  *
16
- * Low-level wrapper around the native `<dialog>` element.
17
- * Manages visibility (`showModal()` / `close()`), animation, Escape handling,
18
- * and backdrop-click detection. Has no visual opinions — no width, height,
19
- * background, border-radius, or layout. Consumers (e.g. `@atlaskit/modal-dialog`)
20
- * provide their own visual styling.
16
+ * The accessible-name requirement (one of `label` or `labelledBy`) is encoded
17
+ * in the discriminated union below - WCAG 4.1.2 violations are caught at
18
+ * compile time instead of in production audit reports.
21
19
  */
22
- export type TDialogProps = {
20
+ type TDialogBaseProps = {
23
21
  children: ReactNode;
24
22
  /**
25
23
  * Whether the dialog is open.
@@ -28,13 +26,13 @@ export type TDialogProps = {
28
26
  * - `false`: calls `close()`. When an `animate` preset is provided, the exit
29
27
  * animation plays (via `allow-discrete`) before the dialog becomes logically closed.
30
28
  *
31
- * The element stays mounted in the DOM — visibility is controlled declaratively.
29
+ * The element stays mounted in the DOM. Visibility is controlled declaratively.
32
30
  */
33
31
  isOpen: boolean;
34
32
  /**
35
33
  * Called when the user triggers close via Escape or backdrop click.
36
34
  *
37
- * The dialog does **not** close itself — the consumer decides whether to
35
+ * The dialog does **not** close itself. The consumer decides whether to
38
36
  * set `isOpen={false}` (e.g. gate by reason).
39
37
  */
40
38
  onClose: (args: {
@@ -61,32 +59,60 @@ export type TDialogProps = {
61
59
  * HTML `id` attribute for the `<dialog>` element.
62
60
  */
63
61
  id?: string;
62
+ /**
63
+ * When true, the `::backdrop` pseudo-element is rendered transparent.
64
+ *
65
+ * Useful for stacked dialogs where only the foreground dialog should
66
+ * show a visible backdrop, or when the consumer wants no overlay.
67
+ */
68
+ shouldHideBackdrop?: boolean;
69
+ /**
70
+ * Called after the exit animation completes (or immediately on close when
71
+ * there is no animation or reduced motion is active).
72
+ *
73
+ * Use this for external lifecycle coordination, e.g. firing an
74
+ * `onCloseComplete` callback or notifying ExitingPersistence.
75
+ */
76
+ onExitFinish?: () => void;
77
+ };
78
+ /**
79
+ * Props for the `Dialog` primitive.
80
+ *
81
+ * Low-level wrapper around the native `<dialog>` element.
82
+ * Manages visibility (`showModal()` / `close()`), animation, Escape handling,
83
+ * and backdrop-click detection. Has no visual opinions - no width, height,
84
+ * background, border-radius, or layout. Consumers (e.g. `@atlaskit/modal-dialog`)
85
+ * provide their own visual styling.
86
+ *
87
+ * Discriminated on the accessible-name source: at least one of `label`
88
+ * (aria-label) or `labelledBy` (aria-labelledby) must be provided. This
89
+ * enforces WCAG 4.1.2 at the type level - an unlabelled dialog landmark
90
+ * is not constructible. Mirrors the pattern used by `TPopupContentProps`.
91
+ */
92
+ export type TDialogProps = TDialogBaseProps & ({
64
93
  /**
65
94
  * Accessible label (`aria-label`).
66
95
  *
67
96
  * When provided, `aria-labelledby` is not set.
68
97
  * Use when there is no visible title element to reference.
69
98
  */
70
- label?: string;
99
+ label: string;
71
100
  /**
72
101
  * ID of the element that labels the dialog (`aria-labelledby`).
73
102
  *
74
103
  * Ignored when `label` is provided.
75
104
  */
76
105
  labelledBy?: string;
106
+ } | {
77
107
  /**
78
- * When true, the `::backdrop` pseudo-element is rendered transparent.
108
+ * Accessible label (`aria-label`).
79
109
  *
80
- * Useful for stacked dialogs where only the foreground dialog should
81
- * show a visible backdrop, or when the consumer wants no overlay.
110
+ * Optional when `labelledBy` is provided.
82
111
  */
83
- shouldHideBackdrop?: boolean;
112
+ label?: string;
84
113
  /**
85
- * Called after the exit animation completes (or immediately on close when
86
- * there is no animation or reduced motion is active).
87
- *
88
- * Use this for external lifecycle coordination — e.g. firing an
89
- * `onCloseComplete` callback or notifying ExitingPersistence.
114
+ * ID of the element that labels the dialog (`aria-labelledby`).
90
115
  */
91
- onExitFinish?: () => void;
92
- };
116
+ labelledBy: string;
117
+ });
118
+ export {};
@@ -1,3 +1,10 @@
1
+ /**
2
+ * @deprecated Combined entry-point retained for backwards-compatibility.
3
+ * Prefer the disjoint subpaths so that bundle analysis attributes the cost
4
+ * to the right consumer:
5
+ * - `@atlaskit/top-layer/dialog/create-close-event` for `createCloseEvent`
6
+ * - `@atlaskit/top-layer/popover/create-close-event` for `createPopoverCloseEvent`
7
+ */
1
8
  export { createCloseEvent } from '../dialog';
2
9
  export { createPopoverCloseEvent } from '../popover/create-close-event';
3
10
  export type { TDialogCloseReason } from '../dialog';
@@ -0,0 +1,2 @@
1
+ export { createCloseEvent } from '../../dialog/create-close-event';
2
+ export type { TDialogCloseReason } from '../../dialog/types';
@@ -1,2 +1,3 @@
1
- export { fromLegacyPlacement, placementMapping } from '../placement-map';
1
+ export { fromLegacyPlacement } from '../placement-map';
2
+ export { LEGACY_PLACEMENTS } from '../placement-map/legacy-placements';
2
3
  export type { TLegacyPlacement, TPlacementOptions } from '../placement-map';
@@ -0,0 +1,2 @@
1
+ export { createPopoverCloseEvent } from '../../popover/create-close-event';
2
+ export type { TPopoverCloseReason } from '../../popover/types';
@@ -9,6 +9,14 @@
9
9
  * All public functions accept an optional `filter` callback to further
10
10
  * restrict which focusable elements are included.
11
11
  */
12
+ /**
13
+ * Returns `true` when `document.activeElement` is inside a nested top-layer
14
+ * descendant of `container` (a `[popover]`, `<dialog>`, `[role="dialog"]` or
15
+ * `[role="alertdialog"]` that is itself a descendant of `container`).
16
+ */
17
+ export declare function isNestedLayerFocused({ container }: {
18
+ container: HTMLElement;
19
+ }): boolean;
12
20
  /**
13
21
  * An optional callback to restrict which focusable elements are included.
14
22
  * Return `true` to include the element, `false` to skip it.
@@ -8,13 +8,11 @@
8
8
  * the browser do the math.
9
9
  *
10
10
  * The probe is appended to `container` so it inherits the same containing
11
- * block, font size, and custom-property scope as the popover. We use `width`
12
- * because it accepts any CSS length, including negative `calc()` results
13
- * (negative widths are clamped to 0 — see note below).
11
+ * block, font size, and custom-property scope as the popover.
14
12
  *
15
- * Cost: one synchronous reflow per call. The fallback only resolves on
16
- * placement/offset changes, not on every scroll/resize update, so this is
17
- * not a hot path.
13
+ * Cost: one synchronous reflow on first cacheable resolution per
14
+ * (container, value) pair, then a `WeakMap` lookup. Resolutions involving
15
+ * `var(` are intentionally not cached.
18
16
  */
19
17
  export declare function resolveCssLengthToPixels({ value, container, }: {
20
18
  value: number | string;