@atlaskit/top-layer 4.2.2 → 4.2.3
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.
- package/CHANGELOG.md +9 -0
- package/dist/cjs/internal/anchor-positioning-fallback.js +11 -9
- package/dist/cjs/internal/get-aria-for-trigger.js +3 -2
- package/dist/cjs/internal/use-anchor-position.js +81 -63
- package/dist/cjs/internal/use-width-from-anchor.js +31 -24
- package/dist/es2019/internal/anchor-positioning-fallback.js +11 -9
- package/dist/es2019/internal/get-aria-for-trigger.js +3 -2
- package/dist/es2019/internal/use-anchor-position.js +80 -65
- package/dist/es2019/internal/use-width-from-anchor.js +31 -24
- package/dist/esm/internal/anchor-positioning-fallback.js +11 -9
- package/dist/esm/internal/get-aria-for-trigger.js +3 -2
- package/dist/esm/internal/use-anchor-position.js +81 -63
- package/dist/esm/internal/use-width-from-anchor.js +31 -24
- package/dist/types/internal/get-aria-for-trigger.d.ts +3 -2
- package/dist/types/internal/use-width-from-anchor.d.ts +6 -3
- package/dist/types/popover/types.d.ts +3 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @atlaskit/top-layer
|
|
2
2
|
|
|
3
|
+
## 4.2.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`3f3fc95b7af6b`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/3f3fc95b7af6b) -
|
|
8
|
+
Fixed `offset.crossAxisShift` moving the popover the wrong distance or direction in some
|
|
9
|
+
placements, and restored the documented `useWidthFromAnchor({ mode: 'min-anchor' })` width
|
|
10
|
+
behaviour.
|
|
11
|
+
|
|
3
12
|
## 4.2.2
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
|
@@ -130,23 +130,26 @@ function applyAlignment(_ref2) {
|
|
|
130
130
|
/**
|
|
131
131
|
* Applies the cross-axis shift offset.
|
|
132
132
|
*
|
|
133
|
-
*
|
|
134
|
-
* `
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
133
|
+
* `forwards` always moves the popover toward the cross-axis END and
|
|
134
|
+
* `backwards` toward the START, for every `align` value. See
|
|
135
|
+
* `notes/decisions/placement-offset.md`.
|
|
136
|
+
*
|
|
137
|
+
* This is a coordinate, so the direction is the sign and nothing else. There
|
|
138
|
+
* is deliberately no per-`align` sign inversion here: the CSS path inverts the
|
|
139
|
+
* sign only because a margin on the END side pushes a box the opposite way to
|
|
140
|
+
* a margin on the START side, which is a property of margins rather than of
|
|
141
|
+
* the shift. Applying that inversion to a coordinate moved `align: 'end'`
|
|
142
|
+
* popovers the wrong way.
|
|
138
143
|
*/
|
|
139
144
|
function applyCrossAxisShift(_ref3) {
|
|
140
145
|
var position = _ref3.position,
|
|
141
146
|
axis = _ref3.axis,
|
|
142
|
-
align = _ref3.align,
|
|
143
147
|
crossAxisShift = _ref3.crossAxisShift;
|
|
144
148
|
if (crossAxisShift.value === 0) {
|
|
145
149
|
return position;
|
|
146
150
|
}
|
|
147
151
|
var directionSign = crossAxisShift.direction === 'forwards' ? 1 : -1;
|
|
148
|
-
var
|
|
149
|
-
var delta = crossAxisShift.value * directionSign * sideSign;
|
|
152
|
+
var delta = crossAxisShift.value * directionSign;
|
|
150
153
|
|
|
151
154
|
// Cross-axis is the OPPOSITE of the placement axis.
|
|
152
155
|
if (axis === 'block') {
|
|
@@ -202,7 +205,6 @@ function computeFallbackPosition(_ref4) {
|
|
|
202
205
|
var shiftedPosition = applyCrossAxisShift({
|
|
203
206
|
position: alignedPosition,
|
|
204
207
|
axis: axis,
|
|
205
|
-
align: align,
|
|
206
208
|
crossAxisShift: crossAxisShift
|
|
207
209
|
});
|
|
208
210
|
|
|
@@ -50,8 +50,9 @@ var roleToAriaHasPopup = {
|
|
|
50
50
|
* `menu → "menu"`, `tooltip → undefined`). React omits the attribute when
|
|
51
51
|
* the value is `undefined`.
|
|
52
52
|
* - `aria-expanded` - reflects the current open state.
|
|
53
|
-
* - `aria-controls` - references the popover element by its id
|
|
54
|
-
*
|
|
53
|
+
* - `aria-controls` - references the popover element by its id, even
|
|
54
|
+
* while the popover is closed. The relationship is stable; `aria-expanded`
|
|
55
|
+
* carries the current visibility state.
|
|
55
56
|
*
|
|
56
57
|
* **This function is for click/keyboard-activated popovers only.** For
|
|
57
58
|
* hover-driven tooltips, the trigger should use `aria-describedby` instead.
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.useAnchorPosition = useAnchorPosition;
|
|
8
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
8
9
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
9
10
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
10
11
|
var _react = require("react");
|
|
@@ -81,6 +82,31 @@ var supportsAnchorPositioning = (0, _once.default)(function () {
|
|
|
81
82
|
return CSS.supports('anchor-name', '--a');
|
|
82
83
|
});
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* A single inline style declaration, as consumed by `setStyle`.
|
|
87
|
+
*/
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The custom property mirroring each cross-axis shift margin.
|
|
91
|
+
*
|
|
92
|
+
* These are written for the planned named arrow `@position-try` rules. Nothing
|
|
93
|
+
* reads them yet, and the shift no longer depends on them: the antisymmetric
|
|
94
|
+
* margins written by `crossAxisShiftMargins` survive a cross-axis slide on
|
|
95
|
+
* their own.
|
|
96
|
+
*/
|
|
97
|
+
var CROSS_AXIS_SHIFT_CUSTOM_PROPERTY = {
|
|
98
|
+
'margin-inline-start': '--ds-cross-axis-shift-margin-start',
|
|
99
|
+
'margin-inline-end': '--ds-cross-axis-shift-margin-end',
|
|
100
|
+
'margin-block-start': '--ds-cross-axis-shift-margin-block-start',
|
|
101
|
+
'margin-block-end': '--ds-cross-axis-shift-margin-block-end'
|
|
102
|
+
};
|
|
103
|
+
var ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES = Object.values(CROSS_AXIS_SHIFT_CUSTOM_PROPERTY).map(function (property) {
|
|
104
|
+
return {
|
|
105
|
+
property: property,
|
|
106
|
+
value: '0px'
|
|
107
|
+
};
|
|
108
|
+
});
|
|
109
|
+
|
|
84
110
|
/**
|
|
85
111
|
* Returns the CSS margin declaration that creates a gap between the
|
|
86
112
|
* popover and its trigger on the side facing the anchor.
|
|
@@ -125,30 +151,54 @@ function edgeMargin(_ref) {
|
|
|
125
151
|
}
|
|
126
152
|
|
|
127
153
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
154
|
+
* **The cross-axis shift margins.**
|
|
155
|
+
*
|
|
156
|
+
* The shift is written antisymmetrically on BOTH cross-axis sides
|
|
157
|
+
* (`start: +value`, `end: -value`) rather than as a single margin on the side
|
|
158
|
+
* picked from `align`. Two separate behaviours require it:
|
|
159
|
+
*
|
|
160
|
+
* 1. **`align: 'center'` is centered with `anchor-center`, which centers the
|
|
161
|
+
* popover's MARGIN box on the anchor.** A single-sided margin widens the
|
|
162
|
+
* margin box, so it displaces the border box by only half its value. An
|
|
163
|
+
* antisymmetric pair leaves the margin box width unchanged (`+value` and
|
|
164
|
+
* `-value` sum to zero) while moving its center by the full value.
|
|
165
|
+
* 2. **`position-try-fallbacks` can slide the popover onto the opposite
|
|
166
|
+
* cross-axis side.** A `<position-area>` fallback keeps the base style's
|
|
167
|
+
* margins, and margin on the un-anchored side has no effect, so a
|
|
168
|
+
* single-sided margin is silently dropped once the browser slides across the
|
|
169
|
+
* cross axis. With both sides written, whichever side ends up anchored moves
|
|
170
|
+
* the popover the same physical direction.
|
|
171
|
+
*
|
|
172
|
+
* Leaving the margin box width unchanged also means the shift does not move
|
|
173
|
+
* the point at which `position-try-fallbacks` decides the popover overflows.
|
|
174
|
+
*
|
|
175
|
+
* A `<try-tactic>` fallback (`flip-block`, `flip-inline`) behaves differently to
|
|
176
|
+
* a `<position-area>` one: it SWAPS the start and end margins rather than
|
|
177
|
+
* keeping them, which is what preserves the gap through a flip. So a flip across
|
|
178
|
+
* the cross axis mirrors the shift along with the rest of the placement, rather
|
|
179
|
+
* than preserving its physical direction. Only the diagonal fallback does that,
|
|
180
|
+
* and only for `align: 'start' | 'end'`. See `placementToTryFallbacks`.
|
|
181
|
+
*
|
|
182
|
+
* A positive value always moves the popover toward the cross-axis END, so
|
|
183
|
+
* `direction: 'forwards'` means the same thing for every `align` value. See
|
|
184
|
+
* `notes/decisions/placement-offset.md`.
|
|
133
185
|
*/
|
|
134
|
-
function
|
|
186
|
+
function crossAxisShiftMargins(_ref2) {
|
|
135
187
|
var placement = _ref2.placement,
|
|
136
188
|
crossAxisShiftCssValue = _ref2.crossAxisShiftCssValue,
|
|
137
189
|
direction = _ref2.direction;
|
|
138
|
-
var
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
var
|
|
142
|
-
var
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
value: value
|
|
151
|
-
};
|
|
190
|
+
var crossAxis = placement.axis === 'block' ? 'inline' : 'block';
|
|
191
|
+
// Wrap any string in calc() with the sign factor. CSS handles the math,
|
|
192
|
+
// so opaque values such as design tokens negate correctly.
|
|
193
|
+
var negated = "calc(-1 * ".concat(crossAxisShiftCssValue, ")");
|
|
194
|
+
var isForwards = direction === 'forwards';
|
|
195
|
+
return [{
|
|
196
|
+
property: "margin-".concat(crossAxis, "-start"),
|
|
197
|
+
value: isForwards ? crossAxisShiftCssValue : negated
|
|
198
|
+
}, {
|
|
199
|
+
property: "margin-".concat(crossAxis, "-end"),
|
|
200
|
+
value: isForwards ? negated : crossAxisShiftCssValue
|
|
201
|
+
}];
|
|
152
202
|
}
|
|
153
203
|
|
|
154
204
|
/**
|
|
@@ -233,10 +283,9 @@ function useAnchorPosition(_ref3) {
|
|
|
233
283
|
placement: stablePlacement,
|
|
234
284
|
offset: gapCssValue
|
|
235
285
|
});
|
|
236
|
-
// Computed once;
|
|
237
|
-
// `--ds-cross-axis-shift-margin-*` custom
|
|
238
|
-
|
|
239
|
-
var crossAxisShift = crossAxisShiftMargin({
|
|
286
|
+
// Computed once; also mirrored into the
|
|
287
|
+
// `--ds-cross-axis-shift-margin-*` custom properties below.
|
|
288
|
+
var crossAxisShift = crossAxisShiftMargins({
|
|
240
289
|
placement: stablePlacement,
|
|
241
290
|
crossAxisShiftCssValue: crossAxisShiftCssValue,
|
|
242
291
|
direction: stablePlacement.offset.crossAxisShift.direction
|
|
@@ -266,45 +315,14 @@ function useAnchorPosition(_ref3) {
|
|
|
266
315
|
}, {
|
|
267
316
|
property: gap.property,
|
|
268
317
|
value: gap.value
|
|
269
|
-
}, {
|
|
270
|
-
property
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
popoverStyles.push({
|
|
278
|
-
property: '--ds-cross-axis-shift-margin-start',
|
|
279
|
-
value: '0px'
|
|
280
|
-
});
|
|
281
|
-
popoverStyles.push({
|
|
282
|
-
property: '--ds-cross-axis-shift-margin-end',
|
|
283
|
-
value: '0px'
|
|
284
|
-
});
|
|
285
|
-
popoverStyles.push({
|
|
286
|
-
property: '--ds-cross-axis-shift-margin-block-start',
|
|
287
|
-
value: '0px'
|
|
288
|
-
});
|
|
289
|
-
popoverStyles.push({
|
|
290
|
-
property: '--ds-cross-axis-shift-margin-block-end',
|
|
291
|
-
value: '0px'
|
|
292
|
-
});
|
|
293
|
-
// `crossAxisShift` IS the active margin; reuse instead of recomputing.
|
|
294
|
-
var crossAxisShiftActive = crossAxisShift;
|
|
295
|
-
var customPropertyByMarginProperty = {
|
|
296
|
-
'margin-inline-start': '--ds-cross-axis-shift-margin-start',
|
|
297
|
-
'margin-inline-end': '--ds-cross-axis-shift-margin-end',
|
|
298
|
-
'margin-block-start': '--ds-cross-axis-shift-margin-block-start',
|
|
299
|
-
'margin-block-end': '--ds-cross-axis-shift-margin-block-end'
|
|
300
|
-
};
|
|
301
|
-
var crossAxisShiftCustomProperty = customPropertyByMarginProperty[crossAxisShiftActive.property];
|
|
302
|
-
if (crossAxisShiftCustomProperty) {
|
|
303
|
-
popoverStyles.push({
|
|
304
|
-
property: crossAxisShiftCustomProperty,
|
|
305
|
-
value: crossAxisShiftActive.value
|
|
306
|
-
});
|
|
307
|
-
}
|
|
318
|
+
}].concat((0, _toConsumableArray2.default)(crossAxisShift), (0, _toConsumableArray2.default)(ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES), (0, _toConsumableArray2.default)(crossAxisShift.map(function (_ref4) {
|
|
319
|
+
var property = _ref4.property,
|
|
320
|
+
value = _ref4.value;
|
|
321
|
+
return {
|
|
322
|
+
property: CROSS_AXIS_SHIFT_CUSTOM_PROPERTY[property],
|
|
323
|
+
value: value
|
|
324
|
+
};
|
|
325
|
+
})));
|
|
308
326
|
|
|
309
327
|
/**
|
|
310
328
|
* **We are never cleaning up anchor names**
|
|
@@ -10,9 +10,12 @@ var _setStyle = require("./set-style");
|
|
|
10
10
|
* Sets the width of a popover element relative to its anchor element.
|
|
11
11
|
*
|
|
12
12
|
* - `'match-anchor'` → `width: anchor-size(width)` - popover matches anchor width exactly.
|
|
13
|
-
* - `'min-anchor'` → `min-
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* - `'min-anchor'` → `min-inline-size: anchor-size(width)` - popover is at least as wide as the
|
|
14
|
+
* anchor, and can grow wider if its content requires it. The anchor width is the ONLY floor: see
|
|
15
|
+
* `notes/decisions/width-from-anchor-floors.md` for why this mode deliberately does not also floor
|
|
16
|
+
* the popover at its own content width.
|
|
17
|
+
* - `'none'` → `min-inline-size: max-content` - no anchor-relative width, but floored at the
|
|
18
|
+
* popover's own content width (default behaviour)
|
|
16
19
|
*
|
|
17
20
|
* Uses CSS `anchor-size(width)` when the browser supports it, falling back to a
|
|
18
21
|
* one-off measurement of `anchor.offsetWidth`.
|
|
@@ -61,22 +64,24 @@ function useWidthFromAnchor(_ref) {
|
|
|
61
64
|
}
|
|
62
65
|
return;
|
|
63
66
|
}
|
|
64
|
-
|
|
65
|
-
// For 'min-anchor' and 'none', apply `min-inline-size: max-content` to ensure
|
|
66
|
-
// correct position-try-fallbacks behaviour (popover margin box overflows the
|
|
67
|
-
// viewport when its span region is too narrow, rather than wrapping content).
|
|
68
|
-
var minInlineSizeStyle = {
|
|
69
|
-
property: 'min-inline-size',
|
|
70
|
-
value: 'max-content'
|
|
71
|
-
};
|
|
72
67
|
if (mode === 'min-anchor') {
|
|
73
|
-
//
|
|
74
|
-
//
|
|
68
|
+
// One declaration, one floor: the anchor width, as a lower bound and not an
|
|
69
|
+
// exact size, which is what separates 'min-anchor' from 'match-anchor'.
|
|
70
|
+
//
|
|
71
|
+
// This mode deliberately does NOT also floor the popover at its own content
|
|
72
|
+
// width. Mode 'none' does, to make a too-narrow span overflow and drive
|
|
73
|
+
// `position-try-fallbacks`, but that floor can only be expressed on
|
|
74
|
+
// `min-inline-size` or on `width`, and both collide with the anchor floor here:
|
|
75
|
+
// `min-inline-size` IS `min-width` in a horizontal writing mode, and `width` is
|
|
76
|
+
// an exact size, so it would cap the popover and override a consumer width.
|
|
77
|
+
// The anchor floor already guarantees a usable width for this mode, so the
|
|
78
|
+
// popover is allowed to wrap down to it. See
|
|
79
|
+
// `notes/decisions/width-from-anchor-floors.md`.
|
|
75
80
|
if (supportsAnchorSize) {
|
|
76
81
|
return (0, _setStyle.setStyle)({
|
|
77
82
|
element: node,
|
|
78
|
-
styles: [
|
|
79
|
-
property: 'min-
|
|
83
|
+
styles: [{
|
|
84
|
+
property: 'min-inline-size',
|
|
80
85
|
value: 'anchor-size(width)'
|
|
81
86
|
}]
|
|
82
87
|
});
|
|
@@ -85,24 +90,26 @@ function useWidthFromAnchor(_ref) {
|
|
|
85
90
|
if (_anchor) {
|
|
86
91
|
return (0, _setStyle.setStyle)({
|
|
87
92
|
element: node,
|
|
88
|
-
styles: [
|
|
89
|
-
property: 'min-
|
|
93
|
+
styles: [{
|
|
94
|
+
property: 'min-inline-size',
|
|
90
95
|
value: "".concat(_anchor.offsetWidth, "px")
|
|
91
96
|
}]
|
|
92
97
|
});
|
|
93
98
|
}
|
|
94
99
|
|
|
95
|
-
// No anchor
|
|
96
|
-
return
|
|
97
|
-
element: node,
|
|
98
|
-
styles: [minInlineSizeStyle]
|
|
99
|
-
});
|
|
100
|
+
// No anchor to measure in the JS fallback, so there is no floor to apply.
|
|
101
|
+
return;
|
|
100
102
|
}
|
|
101
103
|
|
|
102
|
-
// 'none'
|
|
104
|
+
// 'none' has no anchor floor to collide with, so it can carry the content floor.
|
|
105
|
+
// `@atlaskit/popper` depends on the property: its fit-to-viewport mode neutralises
|
|
106
|
+
// this floor with `min-inline-size: 0`.
|
|
103
107
|
return (0, _setStyle.setStyle)({
|
|
104
108
|
element: node,
|
|
105
|
-
styles: [
|
|
109
|
+
styles: [{
|
|
110
|
+
property: 'min-inline-size',
|
|
111
|
+
value: 'max-content'
|
|
112
|
+
}]
|
|
106
113
|
});
|
|
107
114
|
}, [mode, popoverRef, anchorRef, isOpen]);
|
|
108
115
|
}
|
|
@@ -126,24 +126,27 @@ function applyAlignment({
|
|
|
126
126
|
/**
|
|
127
127
|
* Applies the cross-axis shift offset.
|
|
128
128
|
*
|
|
129
|
-
*
|
|
130
|
-
* `
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
129
|
+
* `forwards` always moves the popover toward the cross-axis END and
|
|
130
|
+
* `backwards` toward the START, for every `align` value. See
|
|
131
|
+
* `notes/decisions/placement-offset.md`.
|
|
132
|
+
*
|
|
133
|
+
* This is a coordinate, so the direction is the sign and nothing else. There
|
|
134
|
+
* is deliberately no per-`align` sign inversion here: the CSS path inverts the
|
|
135
|
+
* sign only because a margin on the END side pushes a box the opposite way to
|
|
136
|
+
* a margin on the START side, which is a property of margins rather than of
|
|
137
|
+
* the shift. Applying that inversion to a coordinate moved `align: 'end'`
|
|
138
|
+
* popovers the wrong way.
|
|
134
139
|
*/
|
|
135
140
|
function applyCrossAxisShift({
|
|
136
141
|
position,
|
|
137
142
|
axis,
|
|
138
|
-
align,
|
|
139
143
|
crossAxisShift
|
|
140
144
|
}) {
|
|
141
145
|
if (crossAxisShift.value === 0) {
|
|
142
146
|
return position;
|
|
143
147
|
}
|
|
144
148
|
const directionSign = crossAxisShift.direction === 'forwards' ? 1 : -1;
|
|
145
|
-
const
|
|
146
|
-
const delta = crossAxisShift.value * directionSign * sideSign;
|
|
149
|
+
const delta = crossAxisShift.value * directionSign;
|
|
147
150
|
|
|
148
151
|
// Cross-axis is the OPPOSITE of the placement axis.
|
|
149
152
|
if (axis === 'block') {
|
|
@@ -204,7 +207,6 @@ export function computeFallbackPosition({
|
|
|
204
207
|
const shiftedPosition = applyCrossAxisShift({
|
|
205
208
|
position: alignedPosition,
|
|
206
209
|
axis,
|
|
207
|
-
align,
|
|
208
210
|
crossAxisShift
|
|
209
211
|
});
|
|
210
212
|
|
|
@@ -44,8 +44,9 @@ const roleToAriaHasPopup = {
|
|
|
44
44
|
* `menu → "menu"`, `tooltip → undefined`). React omits the attribute when
|
|
45
45
|
* the value is `undefined`.
|
|
46
46
|
* - `aria-expanded` - reflects the current open state.
|
|
47
|
-
* - `aria-controls` - references the popover element by its id
|
|
48
|
-
*
|
|
47
|
+
* - `aria-controls` - references the popover element by its id, even
|
|
48
|
+
* while the popover is closed. The relationship is stable; `aria-expanded`
|
|
49
|
+
* carries the current visibility state.
|
|
49
50
|
*
|
|
50
51
|
* **This function is for click/keyboard-activated popovers only.** For
|
|
51
52
|
* hover-driven tooltips, the trigger should use `aria-describedby` instead.
|
|
@@ -62,6 +62,29 @@ const supportsAnchorPositioning = once(() => {
|
|
|
62
62
|
return CSS.supports('anchor-name', '--a');
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* A single inline style declaration, as consumed by `setStyle`.
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The custom property mirroring each cross-axis shift margin.
|
|
71
|
+
*
|
|
72
|
+
* These are written for the planned named arrow `@position-try` rules. Nothing
|
|
73
|
+
* reads them yet, and the shift no longer depends on them: the antisymmetric
|
|
74
|
+
* margins written by `crossAxisShiftMargins` survive a cross-axis slide on
|
|
75
|
+
* their own.
|
|
76
|
+
*/
|
|
77
|
+
const CROSS_AXIS_SHIFT_CUSTOM_PROPERTY = {
|
|
78
|
+
'margin-inline-start': '--ds-cross-axis-shift-margin-start',
|
|
79
|
+
'margin-inline-end': '--ds-cross-axis-shift-margin-end',
|
|
80
|
+
'margin-block-start': '--ds-cross-axis-shift-margin-block-start',
|
|
81
|
+
'margin-block-end': '--ds-cross-axis-shift-margin-block-end'
|
|
82
|
+
};
|
|
83
|
+
const ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES = Object.values(CROSS_AXIS_SHIFT_CUSTOM_PROPERTY).map(property => ({
|
|
84
|
+
property,
|
|
85
|
+
value: '0px'
|
|
86
|
+
}));
|
|
87
|
+
|
|
65
88
|
/**
|
|
66
89
|
* Returns the CSS margin declaration that creates a gap between the
|
|
67
90
|
* popover and its trigger on the side facing the anchor.
|
|
@@ -109,33 +132,55 @@ function edgeMargin({
|
|
|
109
132
|
}
|
|
110
133
|
|
|
111
134
|
/**
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
135
|
+
* **The cross-axis shift margins.**
|
|
136
|
+
*
|
|
137
|
+
* The shift is written antisymmetrically on BOTH cross-axis sides
|
|
138
|
+
* (`start: +value`, `end: -value`) rather than as a single margin on the side
|
|
139
|
+
* picked from `align`. Two separate behaviours require it:
|
|
140
|
+
*
|
|
141
|
+
* 1. **`align: 'center'` is centered with `anchor-center`, which centers the
|
|
142
|
+
* popover's MARGIN box on the anchor.** A single-sided margin widens the
|
|
143
|
+
* margin box, so it displaces the border box by only half its value. An
|
|
144
|
+
* antisymmetric pair leaves the margin box width unchanged (`+value` and
|
|
145
|
+
* `-value` sum to zero) while moving its center by the full value.
|
|
146
|
+
* 2. **`position-try-fallbacks` can slide the popover onto the opposite
|
|
147
|
+
* cross-axis side.** A `<position-area>` fallback keeps the base style's
|
|
148
|
+
* margins, and margin on the un-anchored side has no effect, so a
|
|
149
|
+
* single-sided margin is silently dropped once the browser slides across the
|
|
150
|
+
* cross axis. With both sides written, whichever side ends up anchored moves
|
|
151
|
+
* the popover the same physical direction.
|
|
152
|
+
*
|
|
153
|
+
* Leaving the margin box width unchanged also means the shift does not move
|
|
154
|
+
* the point at which `position-try-fallbacks` decides the popover overflows.
|
|
155
|
+
*
|
|
156
|
+
* A `<try-tactic>` fallback (`flip-block`, `flip-inline`) behaves differently to
|
|
157
|
+
* a `<position-area>` one: it SWAPS the start and end margins rather than
|
|
158
|
+
* keeping them, which is what preserves the gap through a flip. So a flip across
|
|
159
|
+
* the cross axis mirrors the shift along with the rest of the placement, rather
|
|
160
|
+
* than preserving its physical direction. Only the diagonal fallback does that,
|
|
161
|
+
* and only for `align: 'start' | 'end'`. See `placementToTryFallbacks`.
|
|
162
|
+
*
|
|
163
|
+
* A positive value always moves the popover toward the cross-axis END, so
|
|
164
|
+
* `direction: 'forwards'` means the same thing for every `align` value. See
|
|
165
|
+
* `notes/decisions/placement-offset.md`.
|
|
117
166
|
*/
|
|
118
|
-
function
|
|
167
|
+
function crossAxisShiftMargins({
|
|
119
168
|
placement,
|
|
120
169
|
crossAxisShiftCssValue,
|
|
121
170
|
direction
|
|
122
171
|
}) {
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return {
|
|
136
|
-
property: `margin-${crossAxis}-${side}`,
|
|
137
|
-
value
|
|
138
|
-
};
|
|
172
|
+
const crossAxis = placement.axis === 'block' ? 'inline' : 'block';
|
|
173
|
+
// Wrap any string in calc() with the sign factor. CSS handles the math,
|
|
174
|
+
// so opaque values such as design tokens negate correctly.
|
|
175
|
+
const negated = `calc(-1 * ${crossAxisShiftCssValue})`;
|
|
176
|
+
const isForwards = direction === 'forwards';
|
|
177
|
+
return [{
|
|
178
|
+
property: `margin-${crossAxis}-start`,
|
|
179
|
+
value: isForwards ? crossAxisShiftCssValue : negated
|
|
180
|
+
}, {
|
|
181
|
+
property: `margin-${crossAxis}-end`,
|
|
182
|
+
value: isForwards ? negated : crossAxisShiftCssValue
|
|
183
|
+
}];
|
|
139
184
|
}
|
|
140
185
|
|
|
141
186
|
/**
|
|
@@ -216,10 +261,9 @@ export function useAnchorPosition({
|
|
|
216
261
|
placement: stablePlacement,
|
|
217
262
|
offset: gapCssValue
|
|
218
263
|
});
|
|
219
|
-
// Computed once;
|
|
220
|
-
// `--ds-cross-axis-shift-margin-*` custom
|
|
221
|
-
|
|
222
|
-
const crossAxisShift = crossAxisShiftMargin({
|
|
264
|
+
// Computed once; also mirrored into the
|
|
265
|
+
// `--ds-cross-axis-shift-margin-*` custom properties below.
|
|
266
|
+
const crossAxisShift = crossAxisShiftMargins({
|
|
223
267
|
placement: stablePlacement,
|
|
224
268
|
crossAxisShiftCssValue,
|
|
225
269
|
direction: stablePlacement.offset.crossAxisShift.direction
|
|
@@ -249,45 +293,16 @@ export function useAnchorPosition({
|
|
|
249
293
|
}, {
|
|
250
294
|
property: gap.property,
|
|
251
295
|
value: gap.value
|
|
252
|
-
},
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
value: '0px'
|
|
263
|
-
});
|
|
264
|
-
popoverStyles.push({
|
|
265
|
-
property: '--ds-cross-axis-shift-margin-end',
|
|
266
|
-
value: '0px'
|
|
267
|
-
});
|
|
268
|
-
popoverStyles.push({
|
|
269
|
-
property: '--ds-cross-axis-shift-margin-block-start',
|
|
270
|
-
value: '0px'
|
|
271
|
-
});
|
|
272
|
-
popoverStyles.push({
|
|
273
|
-
property: '--ds-cross-axis-shift-margin-block-end',
|
|
274
|
-
value: '0px'
|
|
275
|
-
});
|
|
276
|
-
// `crossAxisShift` IS the active margin; reuse instead of recomputing.
|
|
277
|
-
const crossAxisShiftActive = crossAxisShift;
|
|
278
|
-
const customPropertyByMarginProperty = {
|
|
279
|
-
'margin-inline-start': '--ds-cross-axis-shift-margin-start',
|
|
280
|
-
'margin-inline-end': '--ds-cross-axis-shift-margin-end',
|
|
281
|
-
'margin-block-start': '--ds-cross-axis-shift-margin-block-start',
|
|
282
|
-
'margin-block-end': '--ds-cross-axis-shift-margin-block-end'
|
|
283
|
-
};
|
|
284
|
-
const crossAxisShiftCustomProperty = customPropertyByMarginProperty[crossAxisShiftActive.property];
|
|
285
|
-
if (crossAxisShiftCustomProperty) {
|
|
286
|
-
popoverStyles.push({
|
|
287
|
-
property: crossAxisShiftCustomProperty,
|
|
288
|
-
value: crossAxisShiftActive.value
|
|
289
|
-
});
|
|
290
|
-
}
|
|
296
|
+
}, ...crossAxisShift,
|
|
297
|
+
// Zero every side first so the two sides that are NOT on the
|
|
298
|
+
// active cross axis do not keep a value from a previous placement.
|
|
299
|
+
...ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES, ...crossAxisShift.map(({
|
|
300
|
+
property,
|
|
301
|
+
value
|
|
302
|
+
}) => ({
|
|
303
|
+
property: CROSS_AXIS_SHIFT_CUSTOM_PROPERTY[property],
|
|
304
|
+
value
|
|
305
|
+
}))];
|
|
291
306
|
|
|
292
307
|
/**
|
|
293
308
|
* **We are never cleaning up anchor names**
|
|
@@ -5,9 +5,12 @@ import { setStyle } from './set-style';
|
|
|
5
5
|
* Sets the width of a popover element relative to its anchor element.
|
|
6
6
|
*
|
|
7
7
|
* - `'match-anchor'` → `width: anchor-size(width)` - popover matches anchor width exactly.
|
|
8
|
-
* - `'min-anchor'` → `min-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* - `'min-anchor'` → `min-inline-size: anchor-size(width)` - popover is at least as wide as the
|
|
9
|
+
* anchor, and can grow wider if its content requires it. The anchor width is the ONLY floor: see
|
|
10
|
+
* `notes/decisions/width-from-anchor-floors.md` for why this mode deliberately does not also floor
|
|
11
|
+
* the popover at its own content width.
|
|
12
|
+
* - `'none'` → `min-inline-size: max-content` - no anchor-relative width, but floored at the
|
|
13
|
+
* popover's own content width (default behaviour)
|
|
11
14
|
*
|
|
12
15
|
* Uses CSS `anchor-size(width)` when the browser supports it, falling back to a
|
|
13
16
|
* one-off measurement of `anchor.offsetWidth`.
|
|
@@ -57,22 +60,24 @@ export function useWidthFromAnchor({
|
|
|
57
60
|
}
|
|
58
61
|
return;
|
|
59
62
|
}
|
|
60
|
-
|
|
61
|
-
// For 'min-anchor' and 'none', apply `min-inline-size: max-content` to ensure
|
|
62
|
-
// correct position-try-fallbacks behaviour (popover margin box overflows the
|
|
63
|
-
// viewport when its span region is too narrow, rather than wrapping content).
|
|
64
|
-
const minInlineSizeStyle = {
|
|
65
|
-
property: 'min-inline-size',
|
|
66
|
-
value: 'max-content'
|
|
67
|
-
};
|
|
68
63
|
if (mode === 'min-anchor') {
|
|
69
|
-
//
|
|
70
|
-
//
|
|
64
|
+
// One declaration, one floor: the anchor width, as a lower bound and not an
|
|
65
|
+
// exact size, which is what separates 'min-anchor' from 'match-anchor'.
|
|
66
|
+
//
|
|
67
|
+
// This mode deliberately does NOT also floor the popover at its own content
|
|
68
|
+
// width. Mode 'none' does, to make a too-narrow span overflow and drive
|
|
69
|
+
// `position-try-fallbacks`, but that floor can only be expressed on
|
|
70
|
+
// `min-inline-size` or on `width`, and both collide with the anchor floor here:
|
|
71
|
+
// `min-inline-size` IS `min-width` in a horizontal writing mode, and `width` is
|
|
72
|
+
// an exact size, so it would cap the popover and override a consumer width.
|
|
73
|
+
// The anchor floor already guarantees a usable width for this mode, so the
|
|
74
|
+
// popover is allowed to wrap down to it. See
|
|
75
|
+
// `notes/decisions/width-from-anchor-floors.md`.
|
|
71
76
|
if (supportsAnchorSize) {
|
|
72
77
|
return setStyle({
|
|
73
78
|
element: node,
|
|
74
|
-
styles: [
|
|
75
|
-
property: 'min-
|
|
79
|
+
styles: [{
|
|
80
|
+
property: 'min-inline-size',
|
|
76
81
|
value: 'anchor-size(width)'
|
|
77
82
|
}]
|
|
78
83
|
});
|
|
@@ -81,24 +86,26 @@ export function useWidthFromAnchor({
|
|
|
81
86
|
if (anchor) {
|
|
82
87
|
return setStyle({
|
|
83
88
|
element: node,
|
|
84
|
-
styles: [
|
|
85
|
-
property: 'min-
|
|
89
|
+
styles: [{
|
|
90
|
+
property: 'min-inline-size',
|
|
86
91
|
value: `${anchor.offsetWidth}px`
|
|
87
92
|
}]
|
|
88
93
|
});
|
|
89
94
|
}
|
|
90
95
|
|
|
91
|
-
// No anchor
|
|
92
|
-
return
|
|
93
|
-
element: node,
|
|
94
|
-
styles: [minInlineSizeStyle]
|
|
95
|
-
});
|
|
96
|
+
// No anchor to measure in the JS fallback, so there is no floor to apply.
|
|
97
|
+
return;
|
|
96
98
|
}
|
|
97
99
|
|
|
98
|
-
// 'none'
|
|
100
|
+
// 'none' has no anchor floor to collide with, so it can carry the content floor.
|
|
101
|
+
// `@atlaskit/popper` depends on the property: its fit-to-viewport mode neutralises
|
|
102
|
+
// this floor with `min-inline-size: 0`.
|
|
99
103
|
return setStyle({
|
|
100
104
|
element: node,
|
|
101
|
-
styles: [
|
|
105
|
+
styles: [{
|
|
106
|
+
property: 'min-inline-size',
|
|
107
|
+
value: 'max-content'
|
|
108
|
+
}]
|
|
102
109
|
});
|
|
103
110
|
}, [mode, popoverRef, anchorRef, isOpen]);
|
|
104
111
|
}
|
|
@@ -123,23 +123,26 @@ function applyAlignment(_ref2) {
|
|
|
123
123
|
/**
|
|
124
124
|
* Applies the cross-axis shift offset.
|
|
125
125
|
*
|
|
126
|
-
*
|
|
127
|
-
* `
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
126
|
+
* `forwards` always moves the popover toward the cross-axis END and
|
|
127
|
+
* `backwards` toward the START, for every `align` value. See
|
|
128
|
+
* `notes/decisions/placement-offset.md`.
|
|
129
|
+
*
|
|
130
|
+
* This is a coordinate, so the direction is the sign and nothing else. There
|
|
131
|
+
* is deliberately no per-`align` sign inversion here: the CSS path inverts the
|
|
132
|
+
* sign only because a margin on the END side pushes a box the opposite way to
|
|
133
|
+
* a margin on the START side, which is a property of margins rather than of
|
|
134
|
+
* the shift. Applying that inversion to a coordinate moved `align: 'end'`
|
|
135
|
+
* popovers the wrong way.
|
|
131
136
|
*/
|
|
132
137
|
function applyCrossAxisShift(_ref3) {
|
|
133
138
|
var position = _ref3.position,
|
|
134
139
|
axis = _ref3.axis,
|
|
135
|
-
align = _ref3.align,
|
|
136
140
|
crossAxisShift = _ref3.crossAxisShift;
|
|
137
141
|
if (crossAxisShift.value === 0) {
|
|
138
142
|
return position;
|
|
139
143
|
}
|
|
140
144
|
var directionSign = crossAxisShift.direction === 'forwards' ? 1 : -1;
|
|
141
|
-
var
|
|
142
|
-
var delta = crossAxisShift.value * directionSign * sideSign;
|
|
145
|
+
var delta = crossAxisShift.value * directionSign;
|
|
143
146
|
|
|
144
147
|
// Cross-axis is the OPPOSITE of the placement axis.
|
|
145
148
|
if (axis === 'block') {
|
|
@@ -195,7 +198,6 @@ export function computeFallbackPosition(_ref4) {
|
|
|
195
198
|
var shiftedPosition = applyCrossAxisShift({
|
|
196
199
|
position: alignedPosition,
|
|
197
200
|
axis: axis,
|
|
198
|
-
align: align,
|
|
199
201
|
crossAxisShift: crossAxisShift
|
|
200
202
|
});
|
|
201
203
|
|
|
@@ -44,8 +44,9 @@ var roleToAriaHasPopup = {
|
|
|
44
44
|
* `menu → "menu"`, `tooltip → undefined`). React omits the attribute when
|
|
45
45
|
* the value is `undefined`.
|
|
46
46
|
* - `aria-expanded` - reflects the current open state.
|
|
47
|
-
* - `aria-controls` - references the popover element by its id
|
|
48
|
-
*
|
|
47
|
+
* - `aria-controls` - references the popover element by its id, even
|
|
48
|
+
* while the popover is closed. The relationship is stable; `aria-expanded`
|
|
49
|
+
* carries the current visibility state.
|
|
49
50
|
*
|
|
50
51
|
* **This function is for click/keyboard-activated popovers only.** For
|
|
51
52
|
* hover-driven tooltips, the trigger should use `aria-describedby` instead.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
1
2
|
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
2
3
|
import _createClass from "@babel/runtime/helpers/createClass";
|
|
3
4
|
/**
|
|
@@ -74,6 +75,31 @@ var supportsAnchorPositioning = once(function () {
|
|
|
74
75
|
return CSS.supports('anchor-name', '--a');
|
|
75
76
|
});
|
|
76
77
|
|
|
78
|
+
/**
|
|
79
|
+
* A single inline style declaration, as consumed by `setStyle`.
|
|
80
|
+
*/
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The custom property mirroring each cross-axis shift margin.
|
|
84
|
+
*
|
|
85
|
+
* These are written for the planned named arrow `@position-try` rules. Nothing
|
|
86
|
+
* reads them yet, and the shift no longer depends on them: the antisymmetric
|
|
87
|
+
* margins written by `crossAxisShiftMargins` survive a cross-axis slide on
|
|
88
|
+
* their own.
|
|
89
|
+
*/
|
|
90
|
+
var CROSS_AXIS_SHIFT_CUSTOM_PROPERTY = {
|
|
91
|
+
'margin-inline-start': '--ds-cross-axis-shift-margin-start',
|
|
92
|
+
'margin-inline-end': '--ds-cross-axis-shift-margin-end',
|
|
93
|
+
'margin-block-start': '--ds-cross-axis-shift-margin-block-start',
|
|
94
|
+
'margin-block-end': '--ds-cross-axis-shift-margin-block-end'
|
|
95
|
+
};
|
|
96
|
+
var ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES = Object.values(CROSS_AXIS_SHIFT_CUSTOM_PROPERTY).map(function (property) {
|
|
97
|
+
return {
|
|
98
|
+
property: property,
|
|
99
|
+
value: '0px'
|
|
100
|
+
};
|
|
101
|
+
});
|
|
102
|
+
|
|
77
103
|
/**
|
|
78
104
|
* Returns the CSS margin declaration that creates a gap between the
|
|
79
105
|
* popover and its trigger on the side facing the anchor.
|
|
@@ -118,30 +144,54 @@ function edgeMargin(_ref) {
|
|
|
118
144
|
}
|
|
119
145
|
|
|
120
146
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
147
|
+
* **The cross-axis shift margins.**
|
|
148
|
+
*
|
|
149
|
+
* The shift is written antisymmetrically on BOTH cross-axis sides
|
|
150
|
+
* (`start: +value`, `end: -value`) rather than as a single margin on the side
|
|
151
|
+
* picked from `align`. Two separate behaviours require it:
|
|
152
|
+
*
|
|
153
|
+
* 1. **`align: 'center'` is centered with `anchor-center`, which centers the
|
|
154
|
+
* popover's MARGIN box on the anchor.** A single-sided margin widens the
|
|
155
|
+
* margin box, so it displaces the border box by only half its value. An
|
|
156
|
+
* antisymmetric pair leaves the margin box width unchanged (`+value` and
|
|
157
|
+
* `-value` sum to zero) while moving its center by the full value.
|
|
158
|
+
* 2. **`position-try-fallbacks` can slide the popover onto the opposite
|
|
159
|
+
* cross-axis side.** A `<position-area>` fallback keeps the base style's
|
|
160
|
+
* margins, and margin on the un-anchored side has no effect, so a
|
|
161
|
+
* single-sided margin is silently dropped once the browser slides across the
|
|
162
|
+
* cross axis. With both sides written, whichever side ends up anchored moves
|
|
163
|
+
* the popover the same physical direction.
|
|
164
|
+
*
|
|
165
|
+
* Leaving the margin box width unchanged also means the shift does not move
|
|
166
|
+
* the point at which `position-try-fallbacks` decides the popover overflows.
|
|
167
|
+
*
|
|
168
|
+
* A `<try-tactic>` fallback (`flip-block`, `flip-inline`) behaves differently to
|
|
169
|
+
* a `<position-area>` one: it SWAPS the start and end margins rather than
|
|
170
|
+
* keeping them, which is what preserves the gap through a flip. So a flip across
|
|
171
|
+
* the cross axis mirrors the shift along with the rest of the placement, rather
|
|
172
|
+
* than preserving its physical direction. Only the diagonal fallback does that,
|
|
173
|
+
* and only for `align: 'start' | 'end'`. See `placementToTryFallbacks`.
|
|
174
|
+
*
|
|
175
|
+
* A positive value always moves the popover toward the cross-axis END, so
|
|
176
|
+
* `direction: 'forwards'` means the same thing for every `align` value. See
|
|
177
|
+
* `notes/decisions/placement-offset.md`.
|
|
126
178
|
*/
|
|
127
|
-
function
|
|
179
|
+
function crossAxisShiftMargins(_ref2) {
|
|
128
180
|
var placement = _ref2.placement,
|
|
129
181
|
crossAxisShiftCssValue = _ref2.crossAxisShiftCssValue,
|
|
130
182
|
direction = _ref2.direction;
|
|
131
|
-
var
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
var
|
|
135
|
-
var
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
value: value
|
|
144
|
-
};
|
|
183
|
+
var crossAxis = placement.axis === 'block' ? 'inline' : 'block';
|
|
184
|
+
// Wrap any string in calc() with the sign factor. CSS handles the math,
|
|
185
|
+
// so opaque values such as design tokens negate correctly.
|
|
186
|
+
var negated = "calc(-1 * ".concat(crossAxisShiftCssValue, ")");
|
|
187
|
+
var isForwards = direction === 'forwards';
|
|
188
|
+
return [{
|
|
189
|
+
property: "margin-".concat(crossAxis, "-start"),
|
|
190
|
+
value: isForwards ? crossAxisShiftCssValue : negated
|
|
191
|
+
}, {
|
|
192
|
+
property: "margin-".concat(crossAxis, "-end"),
|
|
193
|
+
value: isForwards ? negated : crossAxisShiftCssValue
|
|
194
|
+
}];
|
|
145
195
|
}
|
|
146
196
|
|
|
147
197
|
/**
|
|
@@ -226,10 +276,9 @@ export function useAnchorPosition(_ref3) {
|
|
|
226
276
|
placement: stablePlacement,
|
|
227
277
|
offset: gapCssValue
|
|
228
278
|
});
|
|
229
|
-
// Computed once;
|
|
230
|
-
// `--ds-cross-axis-shift-margin-*` custom
|
|
231
|
-
|
|
232
|
-
var crossAxisShift = crossAxisShiftMargin({
|
|
279
|
+
// Computed once; also mirrored into the
|
|
280
|
+
// `--ds-cross-axis-shift-margin-*` custom properties below.
|
|
281
|
+
var crossAxisShift = crossAxisShiftMargins({
|
|
233
282
|
placement: stablePlacement,
|
|
234
283
|
crossAxisShiftCssValue: crossAxisShiftCssValue,
|
|
235
284
|
direction: stablePlacement.offset.crossAxisShift.direction
|
|
@@ -259,45 +308,14 @@ export function useAnchorPosition(_ref3) {
|
|
|
259
308
|
}, {
|
|
260
309
|
property: gap.property,
|
|
261
310
|
value: gap.value
|
|
262
|
-
}, {
|
|
263
|
-
property
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
popoverStyles.push({
|
|
271
|
-
property: '--ds-cross-axis-shift-margin-start',
|
|
272
|
-
value: '0px'
|
|
273
|
-
});
|
|
274
|
-
popoverStyles.push({
|
|
275
|
-
property: '--ds-cross-axis-shift-margin-end',
|
|
276
|
-
value: '0px'
|
|
277
|
-
});
|
|
278
|
-
popoverStyles.push({
|
|
279
|
-
property: '--ds-cross-axis-shift-margin-block-start',
|
|
280
|
-
value: '0px'
|
|
281
|
-
});
|
|
282
|
-
popoverStyles.push({
|
|
283
|
-
property: '--ds-cross-axis-shift-margin-block-end',
|
|
284
|
-
value: '0px'
|
|
285
|
-
});
|
|
286
|
-
// `crossAxisShift` IS the active margin; reuse instead of recomputing.
|
|
287
|
-
var crossAxisShiftActive = crossAxisShift;
|
|
288
|
-
var customPropertyByMarginProperty = {
|
|
289
|
-
'margin-inline-start': '--ds-cross-axis-shift-margin-start',
|
|
290
|
-
'margin-inline-end': '--ds-cross-axis-shift-margin-end',
|
|
291
|
-
'margin-block-start': '--ds-cross-axis-shift-margin-block-start',
|
|
292
|
-
'margin-block-end': '--ds-cross-axis-shift-margin-block-end'
|
|
293
|
-
};
|
|
294
|
-
var crossAxisShiftCustomProperty = customPropertyByMarginProperty[crossAxisShiftActive.property];
|
|
295
|
-
if (crossAxisShiftCustomProperty) {
|
|
296
|
-
popoverStyles.push({
|
|
297
|
-
property: crossAxisShiftCustomProperty,
|
|
298
|
-
value: crossAxisShiftActive.value
|
|
299
|
-
});
|
|
300
|
-
}
|
|
311
|
+
}].concat(_toConsumableArray(crossAxisShift), _toConsumableArray(ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES), _toConsumableArray(crossAxisShift.map(function (_ref4) {
|
|
312
|
+
var property = _ref4.property,
|
|
313
|
+
value = _ref4.value;
|
|
314
|
+
return {
|
|
315
|
+
property: CROSS_AXIS_SHIFT_CUSTOM_PROPERTY[property],
|
|
316
|
+
value: value
|
|
317
|
+
};
|
|
318
|
+
})));
|
|
301
319
|
|
|
302
320
|
/**
|
|
303
321
|
* **We are never cleaning up anchor names**
|
|
@@ -5,9 +5,12 @@ import { setStyle } from './set-style';
|
|
|
5
5
|
* Sets the width of a popover element relative to its anchor element.
|
|
6
6
|
*
|
|
7
7
|
* - `'match-anchor'` → `width: anchor-size(width)` - popover matches anchor width exactly.
|
|
8
|
-
* - `'min-anchor'` → `min-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* - `'min-anchor'` → `min-inline-size: anchor-size(width)` - popover is at least as wide as the
|
|
9
|
+
* anchor, and can grow wider if its content requires it. The anchor width is the ONLY floor: see
|
|
10
|
+
* `notes/decisions/width-from-anchor-floors.md` for why this mode deliberately does not also floor
|
|
11
|
+
* the popover at its own content width.
|
|
12
|
+
* - `'none'` → `min-inline-size: max-content` - no anchor-relative width, but floored at the
|
|
13
|
+
* popover's own content width (default behaviour)
|
|
11
14
|
*
|
|
12
15
|
* Uses CSS `anchor-size(width)` when the browser supports it, falling back to a
|
|
13
16
|
* one-off measurement of `anchor.offsetWidth`.
|
|
@@ -56,22 +59,24 @@ export function useWidthFromAnchor(_ref) {
|
|
|
56
59
|
}
|
|
57
60
|
return;
|
|
58
61
|
}
|
|
59
|
-
|
|
60
|
-
// For 'min-anchor' and 'none', apply `min-inline-size: max-content` to ensure
|
|
61
|
-
// correct position-try-fallbacks behaviour (popover margin box overflows the
|
|
62
|
-
// viewport when its span region is too narrow, rather than wrapping content).
|
|
63
|
-
var minInlineSizeStyle = {
|
|
64
|
-
property: 'min-inline-size',
|
|
65
|
-
value: 'max-content'
|
|
66
|
-
};
|
|
67
62
|
if (mode === 'min-anchor') {
|
|
68
|
-
//
|
|
69
|
-
//
|
|
63
|
+
// One declaration, one floor: the anchor width, as a lower bound and not an
|
|
64
|
+
// exact size, which is what separates 'min-anchor' from 'match-anchor'.
|
|
65
|
+
//
|
|
66
|
+
// This mode deliberately does NOT also floor the popover at its own content
|
|
67
|
+
// width. Mode 'none' does, to make a too-narrow span overflow and drive
|
|
68
|
+
// `position-try-fallbacks`, but that floor can only be expressed on
|
|
69
|
+
// `min-inline-size` or on `width`, and both collide with the anchor floor here:
|
|
70
|
+
// `min-inline-size` IS `min-width` in a horizontal writing mode, and `width` is
|
|
71
|
+
// an exact size, so it would cap the popover and override a consumer width.
|
|
72
|
+
// The anchor floor already guarantees a usable width for this mode, so the
|
|
73
|
+
// popover is allowed to wrap down to it. See
|
|
74
|
+
// `notes/decisions/width-from-anchor-floors.md`.
|
|
70
75
|
if (supportsAnchorSize) {
|
|
71
76
|
return setStyle({
|
|
72
77
|
element: node,
|
|
73
|
-
styles: [
|
|
74
|
-
property: 'min-
|
|
78
|
+
styles: [{
|
|
79
|
+
property: 'min-inline-size',
|
|
75
80
|
value: 'anchor-size(width)'
|
|
76
81
|
}]
|
|
77
82
|
});
|
|
@@ -80,24 +85,26 @@ export function useWidthFromAnchor(_ref) {
|
|
|
80
85
|
if (_anchor) {
|
|
81
86
|
return setStyle({
|
|
82
87
|
element: node,
|
|
83
|
-
styles: [
|
|
84
|
-
property: 'min-
|
|
88
|
+
styles: [{
|
|
89
|
+
property: 'min-inline-size',
|
|
85
90
|
value: "".concat(_anchor.offsetWidth, "px")
|
|
86
91
|
}]
|
|
87
92
|
});
|
|
88
93
|
}
|
|
89
94
|
|
|
90
|
-
// No anchor
|
|
91
|
-
return
|
|
92
|
-
element: node,
|
|
93
|
-
styles: [minInlineSizeStyle]
|
|
94
|
-
});
|
|
95
|
+
// No anchor to measure in the JS fallback, so there is no floor to apply.
|
|
96
|
+
return;
|
|
95
97
|
}
|
|
96
98
|
|
|
97
|
-
// 'none'
|
|
99
|
+
// 'none' has no anchor floor to collide with, so it can carry the content floor.
|
|
100
|
+
// `@atlaskit/popper` depends on the property: its fit-to-viewport mode neutralises
|
|
101
|
+
// this floor with `min-inline-size: 0`.
|
|
98
102
|
return setStyle({
|
|
99
103
|
element: node,
|
|
100
|
-
styles: [
|
|
104
|
+
styles: [{
|
|
105
|
+
property: 'min-inline-size',
|
|
106
|
+
value: 'max-content'
|
|
107
|
+
}]
|
|
101
108
|
});
|
|
102
109
|
}, [mode, popoverRef, anchorRef, isOpen]);
|
|
103
110
|
}
|
|
@@ -65,8 +65,9 @@ type TAriaForTrigger = {
|
|
|
65
65
|
* `menu → "menu"`, `tooltip → undefined`). React omits the attribute when
|
|
66
66
|
* the value is `undefined`.
|
|
67
67
|
* - `aria-expanded` - reflects the current open state.
|
|
68
|
-
* - `aria-controls` - references the popover element by its id
|
|
69
|
-
*
|
|
68
|
+
* - `aria-controls` - references the popover element by its id, even
|
|
69
|
+
* while the popover is closed. The relationship is stable; `aria-expanded`
|
|
70
|
+
* carries the current visibility state.
|
|
70
71
|
*
|
|
71
72
|
* **This function is for click/keyboard-activated popovers only.** For
|
|
72
73
|
* hover-driven tooltips, the trigger should use `aria-describedby` instead.
|
|
@@ -4,9 +4,12 @@ import type { TWidthFromAnchorMode } from '../popover/types';
|
|
|
4
4
|
* Sets the width of a popover element relative to its anchor element.
|
|
5
5
|
*
|
|
6
6
|
* - `'match-anchor'` → `width: anchor-size(width)` - popover matches anchor width exactly.
|
|
7
|
-
* - `'min-anchor'` → `min-
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* - `'min-anchor'` → `min-inline-size: anchor-size(width)` - popover is at least as wide as the
|
|
8
|
+
* anchor, and can grow wider if its content requires it. The anchor width is the ONLY floor: see
|
|
9
|
+
* `notes/decisions/width-from-anchor-floors.md` for why this mode deliberately does not also floor
|
|
10
|
+
* the popover at its own content width.
|
|
11
|
+
* - `'none'` → `min-inline-size: max-content` - no anchor-relative width, but floored at the
|
|
12
|
+
* popover's own content width (default behaviour)
|
|
10
13
|
*
|
|
11
14
|
* Uses CSS `anchor-size(width)` when the browser supports it, falling back to a
|
|
12
15
|
* one-off measurement of `anchor.offsetWidth`.
|
|
@@ -170,7 +170,8 @@ export type TPopoverProps = TPopoverBaseProps & TAriaRoleRequired & ({
|
|
|
170
170
|
* of `anchorRef.current.offsetWidth` when CSS Anchor Positioning
|
|
171
171
|
* is not supported.
|
|
172
172
|
* - `'min-anchor'`: popover is at least as wide as the anchor, but
|
|
173
|
-
* can grow wider if content requires it. Uses
|
|
174
|
-
* Falls back to a one-off
|
|
173
|
+
* can grow wider if content requires it. Uses
|
|
174
|
+
* `min-inline-size: anchor-size(width)`. Falls back to a one-off
|
|
175
|
+
* measurement of the anchor's width.
|
|
175
176
|
*/
|
|
176
177
|
export type TWidthFromAnchorMode = 'none' | 'match-anchor' | 'min-anchor';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/top-layer",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.3",
|
|
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",
|