@atlaskit/top-layer 0.6.2 → 0.7.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 (66) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/README.md +4 -2
  3. package/dist/cjs/arrow/index.js +2 -2
  4. package/dist/cjs/dialog/dialog-content.js +6 -1
  5. package/dist/cjs/entry-points/use-width-from-anchor.js +12 -0
  6. package/dist/cjs/focus/focus.js +4 -4
  7. package/dist/cjs/internal/combine.js +2 -2
  8. package/dist/cjs/internal/use-anchor-position.js +25 -7
  9. package/dist/cjs/internal/use-animated-visibility.js +3 -3
  10. package/dist/cjs/internal/use-focus-wrap.js +7 -7
  11. package/dist/cjs/internal/use-initial-focus.js +1 -1
  12. package/dist/cjs/internal/use-width-from-anchor.js +107 -0
  13. package/dist/cjs/popover/popover.js +12 -10
  14. package/dist/cjs/popup/popup-content.js +10 -37
  15. package/dist/cjs/popup/popup-trigger.js +2 -2
  16. package/dist/cjs/use-simple-light-dismiss/index.js +1 -1
  17. package/dist/es2019/arrow/index.js +2 -2
  18. package/dist/es2019/dialog/dialog-content.js +6 -1
  19. package/dist/es2019/entry-points/use-width-from-anchor.js +1 -0
  20. package/dist/es2019/focus/focus.js +1 -1
  21. package/dist/es2019/internal/combine.js +1 -1
  22. package/dist/es2019/internal/use-anchor-position.js +13 -7
  23. package/dist/es2019/internal/use-animated-visibility.js +3 -3
  24. package/dist/es2019/internal/use-focus-wrap.js +7 -7
  25. package/dist/es2019/internal/use-initial-focus.js +1 -1
  26. package/dist/es2019/internal/use-width-from-anchor.js +103 -0
  27. package/dist/es2019/popover/popover.js +12 -10
  28. package/dist/es2019/popup/popup-content.js +9 -36
  29. package/dist/es2019/popup/popup-trigger.js +2 -2
  30. package/dist/es2019/use-simple-light-dismiss/index.js +1 -1
  31. package/dist/esm/arrow/index.js +2 -2
  32. package/dist/esm/dialog/dialog-content.js +6 -1
  33. package/dist/esm/entry-points/use-width-from-anchor.js +1 -0
  34. package/dist/esm/focus/focus.js +4 -4
  35. package/dist/esm/internal/combine.js +2 -2
  36. package/dist/esm/internal/use-anchor-position.js +25 -7
  37. package/dist/esm/internal/use-animated-visibility.js +3 -3
  38. package/dist/esm/internal/use-focus-wrap.js +7 -7
  39. package/dist/esm/internal/use-initial-focus.js +1 -1
  40. package/dist/esm/internal/use-width-from-anchor.js +102 -0
  41. package/dist/esm/popover/popover.js +12 -10
  42. package/dist/esm/popup/popup-content.js +10 -37
  43. package/dist/esm/popup/popup-trigger.js +2 -2
  44. package/dist/esm/use-simple-light-dismiss/index.js +1 -1
  45. package/dist/types/entry-points/use-width-from-anchor.d.ts +1 -0
  46. package/dist/types/internal/use-anchor-position.d.ts +1 -1
  47. package/dist/types/internal/use-focus-wrap.d.ts +1 -1
  48. package/dist/types/internal/use-width-from-anchor.d.ts +20 -0
  49. package/dist/types/popover/popover.d.ts +3 -1
  50. package/dist/types/popover/types.d.ts +13 -0
  51. package/dist/types/popup/popup-content.d.ts +1 -1
  52. package/dist/types/popup/popup-trigger.d.ts +1 -1
  53. package/dist/types/popup/types.d.ts +8 -8
  54. package/dist/types/use-simple-light-dismiss/index.d.ts +3 -3
  55. package/dist/types-ts4.5/entry-points/use-width-from-anchor.d.ts +1 -0
  56. package/dist/types-ts4.5/internal/use-anchor-position.d.ts +1 -1
  57. package/dist/types-ts4.5/internal/use-focus-wrap.d.ts +1 -1
  58. package/dist/types-ts4.5/internal/use-width-from-anchor.d.ts +20 -0
  59. package/dist/types-ts4.5/popover/popover.d.ts +3 -1
  60. package/dist/types-ts4.5/popover/types.d.ts +13 -0
  61. package/dist/types-ts4.5/popup/popup-content.d.ts +1 -1
  62. package/dist/types-ts4.5/popup/popup-trigger.d.ts +1 -1
  63. package/dist/types-ts4.5/popup/types.d.ts +8 -8
  64. package/dist/types-ts4.5/use-simple-light-dismiss/index.d.ts +3 -3
  65. package/package.json +2 -2
  66. package/use-width-from-anchor/package.json +17 -0
@@ -0,0 +1,102 @@
1
+ import { useLayoutEffect } from 'react';
2
+ import { setStyle } from './set-style';
3
+
4
+ /**
5
+ * Sets the width of a popover element relative to its anchor element.
6
+ *
7
+ * - `'match-anchor'` → `width: anchor-size(width)` — popover matches anchor width exactly.
8
+ * - `'min-anchor'` → `min-width: anchor-size(width)` — popover is at least as wide as anchor,
9
+ * but can grow wider.
10
+ * - `'none'` → no explicit width is set (default behaviour)
11
+ *
12
+ * Uses CSS `anchor-size(width)` when the browser supports it, falling back to a
13
+ * one-off measurement of `anchor.offsetWidth`.
14
+ *
15
+ * Designed to be composed alongside `useAnchorPosition` for top-layer popovers.
16
+ */
17
+ export function useWidthFromAnchor(_ref) {
18
+ var mode = _ref.mode,
19
+ popoverRef = _ref.popoverRef,
20
+ anchorRef = _ref.anchorRef;
21
+ useLayoutEffect(function () {
22
+ var node = popoverRef.current;
23
+ if (!node) {
24
+ return;
25
+ }
26
+
27
+ // Feature-detect `anchor-size()` specifically (not just `anchor-name`)
28
+ // so the JS fallback fires correctly in browsers that support anchor
29
+ // positioning but not anchor sizing.
30
+ var supportsAnchorSize = typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('width', 'anchor-size(width)');
31
+ if (mode === 'match-anchor') {
32
+ // Set an exact width matching the anchor.
33
+ // `min-inline-size` is intentionally NOT set here — it would override the
34
+ // explicit width constraint, allowing the popover to grow past the anchor's width.
35
+ if (supportsAnchorSize) {
36
+ return setStyle({
37
+ el: node,
38
+ styles: [{
39
+ property: 'width',
40
+ value: 'anchor-size(width)'
41
+ }]
42
+ });
43
+ }
44
+
45
+ // JS fallback: one-off read of the anchor's width.
46
+ var anchor = anchorRef === null || anchorRef === void 0 ? void 0 : anchorRef.current;
47
+ if (anchor) {
48
+ return setStyle({
49
+ el: node,
50
+ styles: [{
51
+ property: 'width',
52
+ value: "".concat(anchor.offsetWidth, "px")
53
+ }]
54
+ });
55
+ }
56
+ return;
57
+ }
58
+
59
+ // For 'min-anchor' and 'none', apply `min-inline-size: max-content` to ensure
60
+ // correct position-try-fallbacks behaviour (popover margin box overflows the
61
+ // viewport when its span region is too narrow, rather than wrapping content).
62
+ var minInlineSizeStyle = {
63
+ property: 'min-inline-size',
64
+ value: 'max-content'
65
+ };
66
+ if (mode === 'min-anchor') {
67
+ // Set a minimum width matching the anchor. The popover can grow wider
68
+ // if its content requires it.
69
+ if (supportsAnchorSize) {
70
+ return setStyle({
71
+ el: node,
72
+ styles: [minInlineSizeStyle, {
73
+ property: 'min-width',
74
+ value: 'anchor-size(width)'
75
+ }]
76
+ });
77
+ }
78
+ var _anchor = anchorRef === null || anchorRef === void 0 ? void 0 : anchorRef.current;
79
+ if (_anchor) {
80
+ return setStyle({
81
+ el: node,
82
+ styles: [minInlineSizeStyle, {
83
+ property: 'min-width',
84
+ value: "".concat(_anchor.offsetWidth, "px")
85
+ }]
86
+ });
87
+ }
88
+
89
+ // No anchor available in JS fallback — still apply min-inline-size.
90
+ return setStyle({
91
+ el: node,
92
+ styles: [minInlineSizeStyle]
93
+ });
94
+ }
95
+
96
+ // 'none' mode: no explicit width, but still apply `min-inline-size: max-content`.
97
+ return setStyle({
98
+ el: node,
99
+ styles: [minInlineSizeStyle]
100
+ });
101
+ }, [mode, popoverRef, anchorRef]);
102
+ }
@@ -35,7 +35,9 @@ var styles = {
35
35
  * Unopinionated top-layer primitive.
36
36
  *
37
37
  * Manages only visibility (isOpen) and animation (animate). Has no knowledge
38
- * of positioning — compose with `useAnchorPosition` for anchor positioning.
38
+ * of positioning — compose with `useAnchorPosition` for anchor positioning
39
+ * and optionally `useWidthFromAnchor` for setting the width of a popover element
40
+ * relative to its anchor trigger.
39
41
  *
40
42
  * Visibility is driven by the `isOpen` prop:
41
43
  * - `isOpen={true}` → `showPopover()`, entry animation via `@starting-style`
@@ -117,21 +119,21 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
117
119
  // the browser processes light dismiss), read in the toggle handler.
118
120
  var closeReasonRef = useRef('light-dismiss');
119
121
  var handleToggle = useCallback(function (event) {
120
- var el = ownRef.current;
122
+ var element = ownRef.current;
121
123
  if (event.newState === 'open') {
122
- if (el) {
124
+ if (element) {
123
125
  onOpenChange === null || onOpenChange === void 0 || onOpenChange({
124
126
  isOpen: true,
125
- element: el
127
+ element: element
126
128
  });
127
129
  }
128
130
  return;
129
131
  }
130
132
  if (event.newState === 'closed') {
131
- if (el) {
133
+ if (element) {
132
134
  onOpenChange === null || onOpenChange === void 0 || onOpenChange({
133
135
  isOpen: false,
134
- element: el
136
+ element: element
135
137
  });
136
138
  }
137
139
 
@@ -149,13 +151,13 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
149
151
  }
150
152
  }, [onClose, onOpenChange]);
151
153
  useEffect(function () {
152
- var el = ownRef.current;
153
- if (!el) {
154
+ var element = ownRef.current;
155
+ if (!element) {
154
156
  return;
155
157
  }
156
158
 
157
159
  // Capture-phase: tag Escape before the browser processes light dismiss.
158
- var unbindEscape = bind(el, {
160
+ var unbindEscape = bind(element, {
159
161
  type: 'keydown',
160
162
  listener: function listener(event) {
161
163
  if (event.key === 'Escape') {
@@ -166,7 +168,7 @@ export var Popover = /*#__PURE__*/forwardRef(function Popover(_ref, ref) {
166
168
  capture: true
167
169
  }
168
170
  });
169
- var unbindToggle = bind(el, {
171
+ var unbindToggle = bind(element, {
170
172
  type: 'toggle',
171
173
  listener: handleToggle
172
174
  });
@@ -6,12 +6,13 @@ import mergeRefs from '@atlaskit/ds-lib/merge-refs';
6
6
  import { roleToAriaHasPopup } from '../internal/role-types';
7
7
  import { useAnchorPosition } from '../internal/use-anchor-position';
8
8
  import { usePresetStyles } from '../internal/use-preset-styles';
9
+ import { useWidthFromAnchor } from '../internal/use-width-from-anchor';
9
10
  import { Popover } from '../popover/popover';
10
11
  import { useMaybePopupContext } from './popup-context';
11
12
  /**
12
13
  * Thin context wrapper that composes `Popover` + `useAnchorPosition`.
13
14
  *
14
- * Used inside the `<Popup>` compound — reads triggerRef, placement, isOpen,
15
+ * Used inside the `<Popup>` compound - reads triggerRef, placement, isOpen,
15
16
  * and onClose from context. For standalone usage (tooltip, spotlight, flag),
16
17
  * use `Popover` + `useAnchorPosition` directly.
17
18
  */
@@ -24,8 +25,8 @@ export var PopupContent = /*#__PURE__*/forwardRef(function PopupContent(_ref, re
24
25
  animate = _ref.animate,
25
26
  arrow = _ref.arrow,
26
27
  mode = _ref.mode,
27
- _ref$width = _ref.width,
28
- width = _ref$width === void 0 ? 'content' : _ref$width,
28
+ _ref$widthFromAnchor = _ref.widthFromAnchor,
29
+ widthFromAnchor = _ref$widthFromAnchor === void 0 ? 'none' : _ref$widthFromAnchor,
29
30
  xcss = _ref.xcss,
30
31
  isOpenProp = _ref.isOpen,
31
32
  triggerRefProp = _ref.triggerRef,
@@ -88,7 +89,7 @@ export var PopupContent = /*#__PURE__*/forwardRef(function PopupContent(_ref, re
88
89
  // ── Nested popover focus restoration fallback ──
89
90
  // The browser only captures `previouslyFocusedElement` for the
90
91
  // outermost popover="auto" in the stack. Inner (nested) popovers
91
- // have shouldRestoreFocus=false, so the browser won't restore
92
+ // have shouldRestoreFocus=false, so the browser will not restore
92
93
  // focus when they close.
93
94
  //
94
95
  // We detect this case by checking if focus is still inside the
@@ -131,39 +132,11 @@ export var PopupContent = /*#__PURE__*/forwardRef(function PopupContent(_ref, re
131
132
  forceFallbackPositioning: forceFallbackPositioning,
132
133
  arrow: arrowPreset !== null && arrowPreset !== void 0 ? arrowPreset : undefined
133
134
  });
134
-
135
- // ── Width: match trigger ──
136
- // When width="trigger" or width="min-trigger", size the popover relative
137
- // to the trigger element. Uses CSS `anchor-size(width)` when supported,
138
- // falling back to a one-off measurement of the trigger's offsetWidth.
139
- useLayoutEffect(function () {
140
- if (width === 'content') {
141
- return;
142
- }
143
- var node = popoverRef.current;
144
- if (!node) {
145
- return;
146
- }
147
- var cssProperty = width === 'trigger' ? 'width' : 'min-width';
148
- var anchorValue = 'anchor-size(width)';
149
-
150
- // Feature-detect `anchor-size()` specifically (not just `anchor-name`)
151
- // so the JS fallback fires correctly in browsers that support anchor
152
- // positioning but not anchor sizing.
153
- var supportsAnchorSize = typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('width', 'anchor-size(width)');
154
- if (supportsAnchorSize) {
155
- node.style.setProperty(cssProperty, anchorValue);
156
- } else {
157
- // JS fallback: one-off read of the trigger's width.
158
- var trigger = triggerRef === null || triggerRef === void 0 ? void 0 : triggerRef.current;
159
- if (trigger) {
160
- node.style.setProperty(cssProperty, "".concat(trigger.offsetWidth, "px"));
161
- }
162
- }
163
- return function () {
164
- node.style.removeProperty(cssProperty);
165
- };
166
- }, [width, popoverRef, triggerRef]);
135
+ useWidthFromAnchor({
136
+ mode: widthFromAnchor,
137
+ popoverRef: popoverRef,
138
+ anchorRef: triggerRef
139
+ });
167
140
 
168
141
  // Merge: the popoverRef (shared with trigger + positioning) and the forwarded ref.
169
142
  var combinedRef = mergeRefs([popoverRef, ref]);
@@ -18,7 +18,7 @@ import { usePopupContext } from './popup-context';
18
18
  * from opening.
19
19
  *
20
20
  * If you need Compiled `css` on the trigger, use `Popup.TriggerFunction`
21
- * instead — it hands you raw props via a render-prop and avoids
21
+ * instead - it hands you raw props via a render-prop and avoids
22
22
  * `cloneElement` entirely.
23
23
  */
24
24
  export function PopupTrigger(_ref) {
@@ -32,7 +32,7 @@ export function PopupTrigger(_ref) {
32
32
  var childOnClick = /*#__PURE__*/isValidElement(children) ? children.props.onClick : undefined;
33
33
 
34
34
  // Use a ref to track the latest isOpen value so the click handler
35
- // doesn't need isOpen in its dependency array (which would recreate
35
+ // does not need isOpen in its dependency array (which would recreate
36
36
  // the handler on every open/close, breaking cloneElement identity).
37
37
  var isOpenRef = React.useRef(isOpen);
38
38
  isOpenRef.current = isOpen;
@@ -20,7 +20,7 @@ export function useSimpleLightDismiss(_ref) {
20
20
  var popoverRef = _ref.popoverRef,
21
21
  isOpen = _ref.isOpen,
22
22
  onClose = _ref.onClose;
23
- // Keep a stable ref to onClose so the effect doesn't re-bind on every render.
23
+ // Keep a stable ref to onClose so the effect does not re-bind on every render.
24
24
  var onCloseRef = useRef(onClose);
25
25
  onCloseRef.current = onClose;
26
26
  useEffect(function () {
@@ -0,0 +1 @@
1
+ export { useWidthFromAnchor } from '../internal/use-width-from-anchor';
@@ -132,7 +132,7 @@ export declare function useAnchorPosition({ anchorRef, popoverRef, placement, fo
132
132
  * the preset's named `@position-try` rules (which update both
133
133
  * `position-area` and `margin` on flip) and sets `--ds-arrow-size`.
134
134
  *
135
- * Pass `null` to disable. Has no effect in the JS fallback path —
135
+ * Pass `null` to disable. Has no effect in the JS fallback path -
136
136
  * arrows are CSS-only.
137
137
  */
138
138
  arrow?: TArrowPreset | null;
@@ -8,7 +8,7 @@ import { type RefObject } from 'react';
8
8
  * elements within the container using wrapping navigation.
9
9
  *
10
10
  * Light dismiss (Escape, click outside) continues to work natively
11
- * via `popover="auto"` — this hook only intercepts Tab.
11
+ * via `popover="auto"` - this hook only intercepts Tab.
12
12
  *
13
13
  * Always call this hook unconditionally. The listener is only
14
14
  * attached when the role requires focus wrapping, and is cleaned
@@ -0,0 +1,20 @@
1
+ import { type RefObject } from 'react';
2
+ import type { TWidthFromAnchorMode } from '../popover/types';
3
+ /**
4
+ * Sets the width of a popover element relative to its anchor element.
5
+ *
6
+ * - `'match-anchor'` → `width: anchor-size(width)` — popover matches anchor width exactly.
7
+ * - `'min-anchor'` → `min-width: anchor-size(width)` — popover is at least as wide as anchor,
8
+ * but can grow wider.
9
+ * - `'none'` → no explicit width is set (default behaviour)
10
+ *
11
+ * Uses CSS `anchor-size(width)` when the browser supports it, falling back to a
12
+ * one-off measurement of `anchor.offsetWidth`.
13
+ *
14
+ * Designed to be composed alongside `useAnchorPosition` for top-layer popovers.
15
+ */
16
+ export declare function useWidthFromAnchor({ mode, popoverRef, anchorRef, }: {
17
+ mode: TWidthFromAnchorMode;
18
+ popoverRef: RefObject<HTMLElement | null>;
19
+ anchorRef: RefObject<HTMLElement | null> | undefined;
20
+ }): void;
@@ -8,7 +8,9 @@ import { type TPopoverForwardedProps } from './types';
8
8
  * Unopinionated top-layer primitive.
9
9
  *
10
10
  * Manages only visibility (isOpen) and animation (animate). Has no knowledge
11
- * of positioning — compose with `useAnchorPosition` for anchor positioning.
11
+ * of positioning — compose with `useAnchorPosition` for anchor positioning
12
+ * and optionally `useWidthFromAnchor` for setting the width of a popover element
13
+ * relative to its anchor trigger.
12
14
  *
13
15
  * Visibility is driven by the `isOpen` prop:
14
16
  * - `isOpen={true}` → `showPopover()`, entry animation via `@starting-style`
@@ -157,4 +157,17 @@ export type TPopoverProps = TPopoverBaseProps & TAriaRoleOptional & ({
157
157
  mode: 'manual';
158
158
  onClose?: never;
159
159
  });
160
+ /**
161
+ * The width mode of the popover relative to its anchor element.
162
+ *
163
+ * - `'none'` (default): popover sizes to its content, ignoring the anchor width.
164
+ * - `'match-anchor'`: popover matches the anchor element's width exactly
165
+ * via CSS `anchor-size(width)`. Falls back to a one-off measurement
166
+ * of `anchorRef.current.offsetWidth` when CSS Anchor Positioning
167
+ * is not supported.
168
+ * - `'min-anchor'`: popover is at least as wide as the anchor, but
169
+ * can grow wider if content requires it. Uses `min-width: anchor-size(width)`.
170
+ * Falls back to a one-off measurement of the anchor's width.
171
+ */
172
+ export type TWidthFromAnchorMode = 'none' | 'match-anchor' | 'min-anchor';
160
173
  export {};
@@ -7,7 +7,7 @@ import { type TPopupContentProps } from './types';
7
7
  /**
8
8
  * Thin context wrapper that composes `Popover` + `useAnchorPosition`.
9
9
  *
10
- * Used inside the `<Popup>` compound — reads triggerRef, placement, isOpen,
10
+ * Used inside the `<Popup>` compound - reads triggerRef, placement, isOpen,
11
11
  * and onClose from context. For standalone usage (tooltip, spotlight, flag),
12
12
  * use `Popover` + `useAnchorPosition` directly.
13
13
  */
@@ -17,7 +17,7 @@ import { type TPopupTriggerProps } from './types';
17
17
  * from opening.
18
18
  *
19
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
20
+ * instead - it hands you raw props via a render-prop and avoids
21
21
  * `cloneElement` entirely.
22
22
  */
23
23
  export declare function PopupTrigger({ children }: TPopupTriggerProps): ReactNode;
@@ -4,7 +4,7 @@ import { type TAnimationPreset } from '../animations/types';
4
4
  import { type TArrowPreset } from '../arrow/types';
5
5
  import { type TPlacementOptions } from '../internal/resolve-placement';
6
6
  import { type TRoleRequiringAccessibleName, type TRoleWithImplicitName } from '../internal/role-types';
7
- import { type TPopoverCloseReason } from '../popover/types';
7
+ import { type TPopoverCloseReason, type TWidthFromAnchorMode } from '../popover/types';
8
8
  /**
9
9
  * Describes where to position a popover relative to its trigger. All fields
10
10
  * are optional. The default `{}` means "below the trigger, centered, with a
@@ -115,18 +115,18 @@ type TPopupContentBaseProps = {
115
115
  */
116
116
  ref?: Ref<HTMLDivElement>;
117
117
  /**
118
- * Controls the width of the popover.
118
+ * Controls the width of the popover relative to its anchor element.
119
119
  *
120
- * - `'content'` (default): popover sizes to its content.
121
- * - `'trigger'`: popover matches the trigger element's width exactly
120
+ * - `'none'` (default): popover sizes to its content, ignoring the anchor width.
121
+ * - `'match-anchor'`: popover matches the anchor element's width exactly
122
122
  * via CSS `anchor-size(width)`. Falls back to a one-off measurement
123
- * of `triggerRef.current.offsetWidth` when CSS Anchor Positioning
123
+ * of `anchorRef.current.offsetWidth` when CSS Anchor Positioning
124
124
  * is not supported.
125
- * - `'min-trigger'`: popover is at least as wide as the trigger, but
125
+ * - `'min-anchor'`: popover is at least as wide as the anchor, but
126
126
  * can grow wider if content requires it. Uses `min-width: anchor-size(width)`.
127
- * Falls back to a one-off measurement of the trigger's width.
127
+ * Falls back to a one-off measurement of the anchor's width.
128
128
  */
129
- width?: 'content' | 'trigger' | 'min-trigger';
129
+ widthFromAnchor?: TWidthFromAnchorMode;
130
130
  /**
131
131
  * Applies additional CSS styles to the popover root element using
132
132
  * design-system-safe xcss.
@@ -13,12 +13,12 @@ type TUseSimpleLightDismissOptions = {
13
13
  /**
14
14
  * Called when a light-dismiss event occurs.
15
15
  *
16
- * - `reason: 'escape'` — the user pressed the Escape key.
17
- * - `reason: 'light-dismiss'` — the user clicked outside the popover.
16
+ * - `reason: 'escape'` - the user pressed the Escape key.
17
+ * - `reason: 'light-dismiss'` - the user clicked outside the popover.
18
18
  *
19
19
  * **Note:** This hook provides simple, standalone light dismiss that does not
20
20
  * participate in the browser's `popover="auto"` dismiss stack. Every active
21
- * instance will fire independently — if multiple popovers use this hook,
21
+ * instance will fire independently - if multiple popovers use this hook,
22
22
  * they will all receive dismiss events.
23
23
  */
24
24
  onClose: (args: {
@@ -0,0 +1 @@
1
+ export { useWidthFromAnchor } from '../internal/use-width-from-anchor';
@@ -132,7 +132,7 @@ export declare function useAnchorPosition({ anchorRef, popoverRef, placement, fo
132
132
  * the preset's named `@position-try` rules (which update both
133
133
  * `position-area` and `margin` on flip) and sets `--ds-arrow-size`.
134
134
  *
135
- * Pass `null` to disable. Has no effect in the JS fallback path —
135
+ * Pass `null` to disable. Has no effect in the JS fallback path -
136
136
  * arrows are CSS-only.
137
137
  */
138
138
  arrow?: TArrowPreset | null;
@@ -8,7 +8,7 @@ import { type RefObject } from 'react';
8
8
  * elements within the container using wrapping navigation.
9
9
  *
10
10
  * Light dismiss (Escape, click outside) continues to work natively
11
- * via `popover="auto"` — this hook only intercepts Tab.
11
+ * via `popover="auto"` - this hook only intercepts Tab.
12
12
  *
13
13
  * Always call this hook unconditionally. The listener is only
14
14
  * attached when the role requires focus wrapping, and is cleaned
@@ -0,0 +1,20 @@
1
+ import { type RefObject } from 'react';
2
+ import type { TWidthFromAnchorMode } from '../popover/types';
3
+ /**
4
+ * Sets the width of a popover element relative to its anchor element.
5
+ *
6
+ * - `'match-anchor'` → `width: anchor-size(width)` — popover matches anchor width exactly.
7
+ * - `'min-anchor'` → `min-width: anchor-size(width)` — popover is at least as wide as anchor,
8
+ * but can grow wider.
9
+ * - `'none'` → no explicit width is set (default behaviour)
10
+ *
11
+ * Uses CSS `anchor-size(width)` when the browser supports it, falling back to a
12
+ * one-off measurement of `anchor.offsetWidth`.
13
+ *
14
+ * Designed to be composed alongside `useAnchorPosition` for top-layer popovers.
15
+ */
16
+ export declare function useWidthFromAnchor({ mode, popoverRef, anchorRef, }: {
17
+ mode: TWidthFromAnchorMode;
18
+ popoverRef: RefObject<HTMLElement | null>;
19
+ anchorRef: RefObject<HTMLElement | null> | undefined;
20
+ }): void;
@@ -8,7 +8,9 @@ import { type TPopoverForwardedProps } from './types';
8
8
  * Unopinionated top-layer primitive.
9
9
  *
10
10
  * Manages only visibility (isOpen) and animation (animate). Has no knowledge
11
- * of positioning — compose with `useAnchorPosition` for anchor positioning.
11
+ * of positioning — compose with `useAnchorPosition` for anchor positioning
12
+ * and optionally `useWidthFromAnchor` for setting the width of a popover element
13
+ * relative to its anchor trigger.
12
14
  *
13
15
  * Visibility is driven by the `isOpen` prop:
14
16
  * - `isOpen={true}` → `showPopover()`, entry animation via `@starting-style`
@@ -157,4 +157,17 @@ export type TPopoverProps = TPopoverBaseProps & TAriaRoleOptional & ({
157
157
  mode: 'manual';
158
158
  onClose?: never;
159
159
  });
160
+ /**
161
+ * The width mode of the popover relative to its anchor element.
162
+ *
163
+ * - `'none'` (default): popover sizes to its content, ignoring the anchor width.
164
+ * - `'match-anchor'`: popover matches the anchor element's width exactly
165
+ * via CSS `anchor-size(width)`. Falls back to a one-off measurement
166
+ * of `anchorRef.current.offsetWidth` when CSS Anchor Positioning
167
+ * is not supported.
168
+ * - `'min-anchor'`: popover is at least as wide as the anchor, but
169
+ * can grow wider if content requires it. Uses `min-width: anchor-size(width)`.
170
+ * Falls back to a one-off measurement of the anchor's width.
171
+ */
172
+ export type TWidthFromAnchorMode = 'none' | 'match-anchor' | 'min-anchor';
160
173
  export {};
@@ -7,7 +7,7 @@ import { type TPopupContentProps } from './types';
7
7
  /**
8
8
  * Thin context wrapper that composes `Popover` + `useAnchorPosition`.
9
9
  *
10
- * Used inside the `<Popup>` compound — reads triggerRef, placement, isOpen,
10
+ * Used inside the `<Popup>` compound - reads triggerRef, placement, isOpen,
11
11
  * and onClose from context. For standalone usage (tooltip, spotlight, flag),
12
12
  * use `Popover` + `useAnchorPosition` directly.
13
13
  */
@@ -17,7 +17,7 @@ import { type TPopupTriggerProps } from './types';
17
17
  * from opening.
18
18
  *
19
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
20
+ * instead - it hands you raw props via a render-prop and avoids
21
21
  * `cloneElement` entirely.
22
22
  */
23
23
  export declare function PopupTrigger({ children }: TPopupTriggerProps): ReactNode;
@@ -4,7 +4,7 @@ import { type TAnimationPreset } from '../animations/types';
4
4
  import { type TArrowPreset } from '../arrow/types';
5
5
  import { type TPlacementOptions } from '../internal/resolve-placement';
6
6
  import { type TRoleRequiringAccessibleName, type TRoleWithImplicitName } from '../internal/role-types';
7
- import { type TPopoverCloseReason } from '../popover/types';
7
+ import { type TPopoverCloseReason, type TWidthFromAnchorMode } from '../popover/types';
8
8
  /**
9
9
  * Describes where to position a popover relative to its trigger. All fields
10
10
  * are optional. The default `{}` means "below the trigger, centered, with a
@@ -115,18 +115,18 @@ type TPopupContentBaseProps = {
115
115
  */
116
116
  ref?: Ref<HTMLDivElement>;
117
117
  /**
118
- * Controls the width of the popover.
118
+ * Controls the width of the popover relative to its anchor element.
119
119
  *
120
- * - `'content'` (default): popover sizes to its content.
121
- * - `'trigger'`: popover matches the trigger element's width exactly
120
+ * - `'none'` (default): popover sizes to its content, ignoring the anchor width.
121
+ * - `'match-anchor'`: popover matches the anchor element's width exactly
122
122
  * via CSS `anchor-size(width)`. Falls back to a one-off measurement
123
- * of `triggerRef.current.offsetWidth` when CSS Anchor Positioning
123
+ * of `anchorRef.current.offsetWidth` when CSS Anchor Positioning
124
124
  * is not supported.
125
- * - `'min-trigger'`: popover is at least as wide as the trigger, but
125
+ * - `'min-anchor'`: popover is at least as wide as the anchor, but
126
126
  * can grow wider if content requires it. Uses `min-width: anchor-size(width)`.
127
- * Falls back to a one-off measurement of the trigger's width.
127
+ * Falls back to a one-off measurement of the anchor's width.
128
128
  */
129
- width?: 'content' | 'trigger' | 'min-trigger';
129
+ widthFromAnchor?: TWidthFromAnchorMode;
130
130
  /**
131
131
  * Applies additional CSS styles to the popover root element using
132
132
  * design-system-safe xcss.
@@ -13,12 +13,12 @@ type TUseSimpleLightDismissOptions = {
13
13
  /**
14
14
  * Called when a light-dismiss event occurs.
15
15
  *
16
- * - `reason: 'escape'` — the user pressed the Escape key.
17
- * - `reason: 'light-dismiss'` — the user clicked outside the popover.
16
+ * - `reason: 'escape'` - the user pressed the Escape key.
17
+ * - `reason: 'light-dismiss'` - the user clicked outside the popover.
18
18
  *
19
19
  * **Note:** This hook provides simple, standalone light dismiss that does not
20
20
  * participate in the browser's `popover="auto"` dismiss stack. Every active
21
- * instance will fire independently — if multiple popovers use this hook,
21
+ * instance will fire independently - if multiple popovers use this hook,
22
22
  * they will all receive dismiss events.
23
23
  */
24
24
  onClose: (args: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/top-layer",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "Low-level layering primitives based on popover and dialog",
5
5
  "homepage": "https://atlassian.design/components/top-layer",
6
6
  "repository": "https://bitbucket.org/atlassian/atlassian-frontend-mirror",
@@ -59,7 +59,7 @@
59
59
  "@atlaskit/textfield": "^8.3.0",
60
60
  "@atlassian/react-compiler-gating": "workspace:^",
61
61
  "@atlassian/ssr-tests": "workspace:^",
62
- "@atlassian/testing-library": "^0.5.0",
62
+ "@atlassian/testing-library": "^0.6.0",
63
63
  "@testing-library/jest-dom": "^6.4.5",
64
64
  "@testing-library/react": "^16.3.0",
65
65
  "@types/raf-schd": "^4.0.1",
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@atlaskit/top-layer/use-width-from-anchor",
3
+ "main": "../dist/cjs/entry-points/use-width-from-anchor.js",
4
+ "module": "../dist/esm/entry-points/use-width-from-anchor.js",
5
+ "module:es2019": "../dist/es2019/entry-points/use-width-from-anchor.js",
6
+ "sideEffects": [
7
+ "*.compiled.css"
8
+ ],
9
+ "types": "../dist/types/entry-points/use-width-from-anchor.d.ts",
10
+ "typesVersions": {
11
+ ">=4.5 <5.9": {
12
+ "*": [
13
+ "../dist/types-ts4.5/entry-points/use-width-from-anchor.d.ts"
14
+ ]
15
+ }
16
+ }
17
+ }