@atlaskit/top-layer 4.2.3 → 4.2.4
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 +13 -0
- package/dist/cjs/internal/resolve-placement.js +7 -2
- package/dist/cjs/internal/set-style.js +9 -0
- package/dist/cjs/internal/use-anchor-position.js +16 -32
- package/dist/cjs/internal/use-width-from-anchor.js +59 -50
- package/dist/es2019/internal/anchor-positioning-fallback.js +13 -0
- package/dist/es2019/internal/resolve-placement.js +7 -2
- package/dist/es2019/internal/set-style.js +9 -0
- package/dist/es2019/internal/use-anchor-position.js +16 -32
- package/dist/es2019/internal/use-width-from-anchor.js +54 -45
- package/dist/esm/internal/anchor-positioning-fallback.js +13 -0
- package/dist/esm/internal/resolve-placement.js +7 -2
- package/dist/esm/internal/set-style.js +9 -0
- package/dist/esm/internal/use-anchor-position.js +16 -32
- package/dist/esm/internal/use-width-from-anchor.js +58 -50
- package/dist/types/internal/anchor-positioning-fallback.d.ts +34 -9
- package/dist/types/internal/resolve-placement.d.ts +15 -12
- package/dist/types/internal/set-style.d.ts +13 -4
- package/dist/types/internal/use-width-from-anchor.d.ts +6 -11
- package/dist/types/popover/types.d.ts +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# @atlaskit/top-layer
|
|
2
2
|
|
|
3
|
+
## 4.2.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`41f64e5df21c7`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/41f64e5df21c7) -
|
|
8
|
+
`useWidthFromAnchor` now sizes against `anchor-size(self-inline)` rather than
|
|
9
|
+
`anchor-size(width)`, so an anchor-relative popover width is measured on the same axis as the
|
|
10
|
+
property it is set on. No change in a horizontal writing mode.
|
|
11
|
+
|
|
3
12
|
## 4.2.3
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
|
@@ -20,6 +20,19 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
20
20
|
* position.
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* A viewport-relative position, in pixels. Physical rather than logical
|
|
25
|
+
* because it is written straight to `top` / `left`.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The cross-axis shift with its length already resolved to pixels.
|
|
30
|
+
*
|
|
31
|
+
* Mirrors `TCrossAxisShiftOffset`, whose `value` is an unresolved CSS length
|
|
32
|
+
* string. Only this module needs the pixel form, because only this module does
|
|
33
|
+
* the arithmetic itself.
|
|
34
|
+
*/
|
|
35
|
+
|
|
23
36
|
/**
|
|
24
37
|
* Computes the base {top, left} position based on the primary edge,
|
|
25
38
|
* flipping to the opposite side when there is not enough space.
|
|
@@ -12,10 +12,15 @@ var _resolveCssLength = require("./resolve-css-length");
|
|
|
12
12
|
*/
|
|
13
13
|
var DEFAULT_GAP = "var(--ds-space-100, 8px)";
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Which way along the cross axis a shift moves the popover. `'forwards'` is
|
|
17
|
+
* always toward the cross-axis END and `'backwards'` toward the START, for
|
|
18
|
+
* every `align` value. See `notes/decisions/placement-offset.md`.
|
|
19
|
+
*/
|
|
20
|
+
|
|
15
21
|
/**
|
|
16
22
|
* Cross-axis shift. `value` is a CSS length string (consumer numbers are
|
|
17
|
-
* normalized to `${n}px`).
|
|
18
|
-
* `'backwards'` (toward start).
|
|
23
|
+
* normalized to `${n}px`).
|
|
19
24
|
*/
|
|
20
25
|
|
|
21
26
|
/**
|
|
@@ -4,6 +4,15 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.setStyle = setStyle;
|
|
7
|
+
/**
|
|
8
|
+
* A single inline style declaration.
|
|
9
|
+
*
|
|
10
|
+
* `TProperty` narrows the property name for callers that write a known, closed
|
|
11
|
+
* set of properties. Narrowing it lets a record keyed on those same names be
|
|
12
|
+
* checked for exhaustiveness at compile time, rather than silently resolving to
|
|
13
|
+
* `undefined` for a name that was never mapped.
|
|
14
|
+
*/
|
|
15
|
+
|
|
7
16
|
/**
|
|
8
17
|
* Sets inline styles on an element and returns a cleanup function that
|
|
9
18
|
* restores the prior inline values (so we do not stomp consumer styles).
|
|
@@ -83,7 +83,11 @@ var supportsAnchorPositioning = (0, _once.default)(function () {
|
|
|
83
83
|
});
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
|
-
*
|
|
86
|
+
* Every logical margin property this module can write: one per axis, per side.
|
|
87
|
+
*
|
|
88
|
+
* Deriving the union from `TPlacementAxis` and `TPlacementEdge` rather than
|
|
89
|
+
* writing the four names out keeps it in step with the placement model, and
|
|
90
|
+
* makes `CROSS_AXIS_SHIFT_CUSTOM_PROPERTY` below exhaustive by construction.
|
|
87
91
|
*/
|
|
88
92
|
|
|
89
93
|
/**
|
|
@@ -93,6 +97,11 @@ var supportsAnchorPositioning = (0, _once.default)(function () {
|
|
|
93
97
|
* reads them yet, and the shift no longer depends on them: the antisymmetric
|
|
94
98
|
* margins written by `crossAxisShiftMargins` survive a cross-axis slide on
|
|
95
99
|
* their own.
|
|
100
|
+
*
|
|
101
|
+
* Keyed on `TLogicalMarginProperty` and not on `string`, so a margin property
|
|
102
|
+
* that has no custom property (or a custom property whose margin no longer
|
|
103
|
+
* exists) is a compile error rather than an `undefined` lookup that reaches
|
|
104
|
+
* `setStyle` as `{ property: undefined, value }`.
|
|
96
105
|
*/
|
|
97
106
|
var CROSS_AXIS_SHIFT_CUSTOM_PROPERTY = {
|
|
98
107
|
'margin-inline-start': '--ds-cross-axis-shift-margin-start',
|
|
@@ -119,34 +128,12 @@ function edgeMargin(_ref) {
|
|
|
119
128
|
offset = _ref.offset;
|
|
120
129
|
var axis = placement.axis,
|
|
121
130
|
edge = placement.edge;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
property: 'margin-block-start',
|
|
126
|
-
value: value
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
if (axis === 'block' && edge === 'start') {
|
|
130
|
-
return {
|
|
131
|
-
property: 'margin-block-end',
|
|
132
|
-
value: value
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
if (axis === 'inline' && edge === 'end') {
|
|
136
|
-
return {
|
|
137
|
-
property: 'margin-inline-start',
|
|
138
|
-
value: value
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
if (axis === 'inline' && edge === 'start') {
|
|
142
|
-
return {
|
|
143
|
-
property: 'margin-inline-end',
|
|
144
|
-
value: value
|
|
145
|
-
};
|
|
146
|
-
}
|
|
131
|
+
// The gap sits on the side FACING the anchor, which is always the side
|
|
132
|
+
// opposite the placement edge.
|
|
133
|
+
var anchorFacingEdge = edge === 'end' ? 'start' : 'end';
|
|
147
134
|
return {
|
|
148
|
-
property:
|
|
149
|
-
value:
|
|
135
|
+
property: "margin-".concat(axis, "-").concat(anchorFacingEdge),
|
|
136
|
+
value: offset
|
|
150
137
|
};
|
|
151
138
|
}
|
|
152
139
|
|
|
@@ -312,10 +299,7 @@ function useAnchorPosition(_ref3) {
|
|
|
312
299
|
}, {
|
|
313
300
|
property: 'inset',
|
|
314
301
|
value: 'auto'
|
|
315
|
-
}, {
|
|
316
|
-
property: gap.property,
|
|
317
|
-
value: gap.value
|
|
318
|
-
}].concat((0, _toConsumableArray2.default)(crossAxisShift), (0, _toConsumableArray2.default)(ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES), (0, _toConsumableArray2.default)(crossAxisShift.map(function (_ref4) {
|
|
302
|
+
}, gap].concat((0, _toConsumableArray2.default)(crossAxisShift), (0, _toConsumableArray2.default)(ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES), (0, _toConsumableArray2.default)(crossAxisShift.map(function (_ref4) {
|
|
319
303
|
var property = _ref4.property,
|
|
320
304
|
value = _ref4.value;
|
|
321
305
|
return {
|
|
@@ -1,88 +1,95 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
3
4
|
Object.defineProperty(exports, "__esModule", {
|
|
4
5
|
value: true
|
|
5
6
|
});
|
|
6
7
|
exports.useWidthFromAnchor = useWidthFromAnchor;
|
|
7
8
|
var _react = require("react");
|
|
9
|
+
var _once = _interopRequireDefault(require("@atlaskit/ds-lib/once"));
|
|
8
10
|
var _setStyle = require("./set-style");
|
|
11
|
+
/**
|
|
12
|
+
* Probes anchor SIZING, separately from `anchor-name` (see
|
|
13
|
+
* `supportsAnchorPositioning`): some browsers have positioning but not sizing.
|
|
14
|
+
* `once()` for SSR safety.
|
|
15
|
+
*/
|
|
16
|
+
var supportsAnchorSize = (0, _once.default)(function () {
|
|
17
|
+
if (typeof window === 'undefined' || typeof CSS === 'undefined' || typeof CSS.supports !== 'function') {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return CSS.supports('min-inline-size', 'anchor-size(self-inline)');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The JS fallback's stand-in for `anchor-size(self-inline)`. `offsetWidth` / `offsetHeight` are
|
|
25
|
+
* physical, so the axis is picked from the popover's `writing-mode`. Anything not horizontal is
|
|
26
|
+
* treated as vertical, and an unknown or empty value falls back to `offsetWidth`.
|
|
27
|
+
*/
|
|
28
|
+
function getFallbackAnchorInlineSize(_ref) {
|
|
29
|
+
var anchor = _ref.anchor,
|
|
30
|
+
popover = _ref.popover;
|
|
31
|
+
// `String()` because not every test environment implements `writing-mode`.
|
|
32
|
+
var writingMode = String(getComputedStyle(popover).writingMode);
|
|
33
|
+
var isVertical = writingMode.startsWith('vertical') || writingMode.startsWith('sideways');
|
|
34
|
+
return isVertical ? anchor.offsetHeight : anchor.offsetWidth;
|
|
35
|
+
}
|
|
36
|
+
|
|
9
37
|
/**
|
|
10
38
|
* Sets the width of a popover element relative to its anchor element.
|
|
11
39
|
*
|
|
12
|
-
* - `'match-anchor'` → `
|
|
13
|
-
* - `'min-anchor'` → `min-inline-size: anchor-size(
|
|
14
|
-
*
|
|
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)
|
|
19
|
-
*
|
|
20
|
-
* Uses CSS `anchor-size(width)` when the browser supports it, falling back to a
|
|
21
|
-
* one-off measurement of `anchor.offsetWidth`.
|
|
40
|
+
* - `'match-anchor'` → `inline-size: anchor-size(self-inline)`, exact
|
|
41
|
+
* - `'min-anchor'` → `min-inline-size: anchor-size(self-inline)`, floor only
|
|
42
|
+
* - `'none'` → `min-inline-size: max-content`, content floor (default)
|
|
22
43
|
*
|
|
23
|
-
*
|
|
44
|
+
* `self-inline` is the inline axis of the element USING the function (the popover), never the
|
|
45
|
+
* anchor, so it matches the axis `inline-size` constrains. See
|
|
46
|
+
* `notes/decisions/width-from-anchor-floors.md`.
|
|
24
47
|
*/
|
|
25
|
-
function useWidthFromAnchor(
|
|
26
|
-
var mode =
|
|
27
|
-
popoverRef =
|
|
28
|
-
anchorRef =
|
|
29
|
-
isOpen =
|
|
48
|
+
function useWidthFromAnchor(_ref2) {
|
|
49
|
+
var mode = _ref2.mode,
|
|
50
|
+
popoverRef = _ref2.popoverRef,
|
|
51
|
+
anchorRef = _ref2.anchorRef,
|
|
52
|
+
isOpen = _ref2.isOpen;
|
|
30
53
|
(0, _react.useLayoutEffect)(function () {
|
|
31
54
|
var node = popoverRef.current;
|
|
32
55
|
if (!node) {
|
|
33
56
|
return;
|
|
34
57
|
}
|
|
35
|
-
|
|
36
|
-
// Feature-detect `anchor-size()` specifically (not just `anchor-name`)
|
|
37
|
-
// so the JS fallback fires correctly in browsers that support anchor
|
|
38
|
-
// positioning but not anchor sizing.
|
|
39
|
-
var supportsAnchorSize = typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('width', 'anchor-size(width)');
|
|
40
58
|
if (mode === 'match-anchor') {
|
|
41
|
-
//
|
|
42
|
-
|
|
43
|
-
// explicit width constraint, allowing the popover to grow past the anchor's width.
|
|
44
|
-
if (supportsAnchorSize) {
|
|
59
|
+
// No floor here: it would let the popover grow past the anchor.
|
|
60
|
+
if (supportsAnchorSize()) {
|
|
45
61
|
return (0, _setStyle.setStyle)({
|
|
46
62
|
element: node,
|
|
47
63
|
styles: [{
|
|
48
|
-
property: '
|
|
49
|
-
value: 'anchor-size(
|
|
64
|
+
property: 'inline-size',
|
|
65
|
+
value: 'anchor-size(self-inline)'
|
|
50
66
|
}]
|
|
51
67
|
});
|
|
52
68
|
}
|
|
53
|
-
|
|
54
|
-
// JS fallback: one-off read of the anchor's width.
|
|
55
69
|
var anchor = anchorRef.current;
|
|
56
70
|
if (anchor) {
|
|
57
71
|
return (0, _setStyle.setStyle)({
|
|
58
72
|
element: node,
|
|
59
73
|
styles: [{
|
|
60
|
-
property: '
|
|
61
|
-
value: "".concat(
|
|
74
|
+
property: 'inline-size',
|
|
75
|
+
value: "".concat(getFallbackAnchorInlineSize({
|
|
76
|
+
anchor: anchor,
|
|
77
|
+
popover: node
|
|
78
|
+
}), "px")
|
|
62
79
|
}]
|
|
63
80
|
});
|
|
64
81
|
}
|
|
65
82
|
return;
|
|
66
83
|
}
|
|
67
84
|
if (mode === 'min-anchor') {
|
|
68
|
-
// One
|
|
69
|
-
//
|
|
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`.
|
|
80
|
-
if (supportsAnchorSize) {
|
|
85
|
+
// One floor only. The content floor from mode 'none' deliberately does not also apply
|
|
86
|
+
// here, and cannot: it would need this same property. See the decision note.
|
|
87
|
+
if (supportsAnchorSize()) {
|
|
81
88
|
return (0, _setStyle.setStyle)({
|
|
82
89
|
element: node,
|
|
83
90
|
styles: [{
|
|
84
91
|
property: 'min-inline-size',
|
|
85
|
-
value: 'anchor-size(
|
|
92
|
+
value: 'anchor-size(self-inline)'
|
|
86
93
|
}]
|
|
87
94
|
});
|
|
88
95
|
}
|
|
@@ -92,18 +99,20 @@ function useWidthFromAnchor(_ref) {
|
|
|
92
99
|
element: node,
|
|
93
100
|
styles: [{
|
|
94
101
|
property: 'min-inline-size',
|
|
95
|
-
value: "".concat(
|
|
102
|
+
value: "".concat(getFallbackAnchorInlineSize({
|
|
103
|
+
anchor: _anchor,
|
|
104
|
+
popover: node
|
|
105
|
+
}), "px")
|
|
96
106
|
}]
|
|
97
107
|
});
|
|
98
108
|
}
|
|
99
109
|
|
|
100
|
-
//
|
|
110
|
+
// Nothing to measure, so no floor.
|
|
101
111
|
return;
|
|
102
112
|
}
|
|
103
113
|
|
|
104
|
-
//
|
|
105
|
-
// `@atlaskit/popper`
|
|
106
|
-
// this floor with `min-inline-size: 0`.
|
|
114
|
+
// Makes a too-narrow span overflow and drive `position-try-fallbacks` instead of wrapping.
|
|
115
|
+
// `@atlaskit/popper` resets this exact property to `0` for its fit-to-viewport mode.
|
|
107
116
|
return (0, _setStyle.setStyle)({
|
|
108
117
|
element: node,
|
|
109
118
|
styles: [{
|
|
@@ -10,6 +10,19 @@
|
|
|
10
10
|
* position.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* A viewport-relative position, in pixels. Physical rather than logical
|
|
15
|
+
* because it is written straight to `top` / `left`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The cross-axis shift with its length already resolved to pixels.
|
|
20
|
+
*
|
|
21
|
+
* Mirrors `TCrossAxisShiftOffset`, whose `value` is an unresolved CSS length
|
|
22
|
+
* string. Only this module needs the pixel form, because only this module does
|
|
23
|
+
* the arithmetic itself.
|
|
24
|
+
*/
|
|
25
|
+
|
|
13
26
|
/**
|
|
14
27
|
* Computes the base {top, left} position based on the primary edge,
|
|
15
28
|
* flipping to the opposite side when there is not enough space.
|
|
@@ -7,10 +7,15 @@ import { toCssLengthString } from './resolve-css-length';
|
|
|
7
7
|
*/
|
|
8
8
|
const DEFAULT_GAP = "var(--ds-space-100, 8px)";
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Which way along the cross axis a shift moves the popover. `'forwards'` is
|
|
12
|
+
* always toward the cross-axis END and `'backwards'` toward the START, for
|
|
13
|
+
* every `align` value. See `notes/decisions/placement-offset.md`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
10
16
|
/**
|
|
11
17
|
* Cross-axis shift. `value` is a CSS length string (consumer numbers are
|
|
12
|
-
* normalized to `${n}px`).
|
|
13
|
-
* `'backwards'` (toward start).
|
|
18
|
+
* normalized to `${n}px`).
|
|
14
19
|
*/
|
|
15
20
|
|
|
16
21
|
/**
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single inline style declaration.
|
|
3
|
+
*
|
|
4
|
+
* `TProperty` narrows the property name for callers that write a known, closed
|
|
5
|
+
* set of properties. Narrowing it lets a record keyed on those same names be
|
|
6
|
+
* checked for exhaustiveness at compile time, rather than silently resolving to
|
|
7
|
+
* `undefined` for a name that was never mapped.
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
/**
|
|
2
11
|
* Sets inline styles on an element and returns a cleanup function that
|
|
3
12
|
* restores the prior inline values (so we do not stomp consumer styles).
|
|
@@ -63,7 +63,11 @@ const supportsAnchorPositioning = once(() => {
|
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
*
|
|
66
|
+
* Every logical margin property this module can write: one per axis, per side.
|
|
67
|
+
*
|
|
68
|
+
* Deriving the union from `TPlacementAxis` and `TPlacementEdge` rather than
|
|
69
|
+
* writing the four names out keeps it in step with the placement model, and
|
|
70
|
+
* makes `CROSS_AXIS_SHIFT_CUSTOM_PROPERTY` below exhaustive by construction.
|
|
67
71
|
*/
|
|
68
72
|
|
|
69
73
|
/**
|
|
@@ -73,6 +77,11 @@ const supportsAnchorPositioning = once(() => {
|
|
|
73
77
|
* reads them yet, and the shift no longer depends on them: the antisymmetric
|
|
74
78
|
* margins written by `crossAxisShiftMargins` survive a cross-axis slide on
|
|
75
79
|
* their own.
|
|
80
|
+
*
|
|
81
|
+
* Keyed on `TLogicalMarginProperty` and not on `string`, so a margin property
|
|
82
|
+
* that has no custom property (or a custom property whose margin no longer
|
|
83
|
+
* exists) is a compile error rather than an `undefined` lookup that reaches
|
|
84
|
+
* `setStyle` as `{ property: undefined, value }`.
|
|
76
85
|
*/
|
|
77
86
|
const CROSS_AXIS_SHIFT_CUSTOM_PROPERTY = {
|
|
78
87
|
'margin-inline-start': '--ds-cross-axis-shift-margin-start',
|
|
@@ -100,34 +109,12 @@ function edgeMargin({
|
|
|
100
109
|
axis,
|
|
101
110
|
edge
|
|
102
111
|
} = placement;
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
property: 'margin-block-start',
|
|
107
|
-
value
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
if (axis === 'block' && edge === 'start') {
|
|
111
|
-
return {
|
|
112
|
-
property: 'margin-block-end',
|
|
113
|
-
value
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
if (axis === 'inline' && edge === 'end') {
|
|
117
|
-
return {
|
|
118
|
-
property: 'margin-inline-start',
|
|
119
|
-
value
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
if (axis === 'inline' && edge === 'start') {
|
|
123
|
-
return {
|
|
124
|
-
property: 'margin-inline-end',
|
|
125
|
-
value
|
|
126
|
-
};
|
|
127
|
-
}
|
|
112
|
+
// The gap sits on the side FACING the anchor, which is always the side
|
|
113
|
+
// opposite the placement edge.
|
|
114
|
+
const anchorFacingEdge = edge === 'end' ? 'start' : 'end';
|
|
128
115
|
return {
|
|
129
|
-
property:
|
|
130
|
-
value
|
|
116
|
+
property: `margin-${axis}-${anchorFacingEdge}`,
|
|
117
|
+
value: offset
|
|
131
118
|
};
|
|
132
119
|
}
|
|
133
120
|
|
|
@@ -290,10 +277,7 @@ export function useAnchorPosition({
|
|
|
290
277
|
}, {
|
|
291
278
|
property: 'inset',
|
|
292
279
|
value: 'auto'
|
|
293
|
-
},
|
|
294
|
-
property: gap.property,
|
|
295
|
-
value: gap.value
|
|
296
|
-
}, ...crossAxisShift,
|
|
280
|
+
}, gap, ...crossAxisShift,
|
|
297
281
|
// Zero every side first so the two sides that are NOT on the
|
|
298
282
|
// active cross axis do not keep a value from a previous placement.
|
|
299
283
|
...ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES, ...crossAxisShift.map(({
|
|
@@ -1,21 +1,44 @@
|
|
|
1
1
|
import { useLayoutEffect } from 'react';
|
|
2
|
+
import once from '@atlaskit/ds-lib/once';
|
|
2
3
|
import { setStyle } from './set-style';
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Probes anchor SIZING, separately from `anchor-name` (see
|
|
7
|
+
* `supportsAnchorPositioning`): some browsers have positioning but not sizing.
|
|
8
|
+
* `once()` for SSR safety.
|
|
9
|
+
*/
|
|
10
|
+
const supportsAnchorSize = once(() => {
|
|
11
|
+
if (typeof window === 'undefined' || typeof CSS === 'undefined' || typeof CSS.supports !== 'function') {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return CSS.supports('min-inline-size', 'anchor-size(self-inline)');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The JS fallback's stand-in for `anchor-size(self-inline)`. `offsetWidth` / `offsetHeight` are
|
|
19
|
+
* physical, so the axis is picked from the popover's `writing-mode`. Anything not horizontal is
|
|
20
|
+
* treated as vertical, and an unknown or empty value falls back to `offsetWidth`.
|
|
21
|
+
*/
|
|
22
|
+
function getFallbackAnchorInlineSize({
|
|
23
|
+
anchor,
|
|
24
|
+
popover
|
|
25
|
+
}) {
|
|
26
|
+
// `String()` because not every test environment implements `writing-mode`.
|
|
27
|
+
const writingMode = String(getComputedStyle(popover).writingMode);
|
|
28
|
+
const isVertical = writingMode.startsWith('vertical') || writingMode.startsWith('sideways');
|
|
29
|
+
return isVertical ? anchor.offsetHeight : anchor.offsetWidth;
|
|
30
|
+
}
|
|
31
|
+
|
|
4
32
|
/**
|
|
5
33
|
* Sets the width of a popover element relative to its anchor element.
|
|
6
34
|
*
|
|
7
|
-
* - `'match-anchor'` → `
|
|
8
|
-
* - `'min-anchor'` → `min-inline-size: anchor-size(
|
|
9
|
-
*
|
|
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)
|
|
14
|
-
*
|
|
15
|
-
* Uses CSS `anchor-size(width)` when the browser supports it, falling back to a
|
|
16
|
-
* one-off measurement of `anchor.offsetWidth`.
|
|
35
|
+
* - `'match-anchor'` → `inline-size: anchor-size(self-inline)`, exact
|
|
36
|
+
* - `'min-anchor'` → `min-inline-size: anchor-size(self-inline)`, floor only
|
|
37
|
+
* - `'none'` → `min-inline-size: max-content`, content floor (default)
|
|
17
38
|
*
|
|
18
|
-
*
|
|
39
|
+
* `self-inline` is the inline axis of the element USING the function (the popover), never the
|
|
40
|
+
* anchor, so it matches the axis `inline-size` constrains. See
|
|
41
|
+
* `notes/decisions/width-from-anchor-floors.md`.
|
|
19
42
|
*/
|
|
20
43
|
export function useWidthFromAnchor({
|
|
21
44
|
mode,
|
|
@@ -28,57 +51,41 @@ export function useWidthFromAnchor({
|
|
|
28
51
|
if (!node) {
|
|
29
52
|
return;
|
|
30
53
|
}
|
|
31
|
-
|
|
32
|
-
// Feature-detect `anchor-size()` specifically (not just `anchor-name`)
|
|
33
|
-
// so the JS fallback fires correctly in browsers that support anchor
|
|
34
|
-
// positioning but not anchor sizing.
|
|
35
|
-
const supportsAnchorSize = typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('width', 'anchor-size(width)');
|
|
36
54
|
if (mode === 'match-anchor') {
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
// explicit width constraint, allowing the popover to grow past the anchor's width.
|
|
40
|
-
if (supportsAnchorSize) {
|
|
55
|
+
// No floor here: it would let the popover grow past the anchor.
|
|
56
|
+
if (supportsAnchorSize()) {
|
|
41
57
|
return setStyle({
|
|
42
58
|
element: node,
|
|
43
59
|
styles: [{
|
|
44
|
-
property: '
|
|
45
|
-
value: 'anchor-size(
|
|
60
|
+
property: 'inline-size',
|
|
61
|
+
value: 'anchor-size(self-inline)'
|
|
46
62
|
}]
|
|
47
63
|
});
|
|
48
64
|
}
|
|
49
|
-
|
|
50
|
-
// JS fallback: one-off read of the anchor's width.
|
|
51
65
|
const anchor = anchorRef.current;
|
|
52
66
|
if (anchor) {
|
|
53
67
|
return setStyle({
|
|
54
68
|
element: node,
|
|
55
69
|
styles: [{
|
|
56
|
-
property: '
|
|
57
|
-
value: `${
|
|
70
|
+
property: 'inline-size',
|
|
71
|
+
value: `${getFallbackAnchorInlineSize({
|
|
72
|
+
anchor,
|
|
73
|
+
popover: node
|
|
74
|
+
})}px`
|
|
58
75
|
}]
|
|
59
76
|
});
|
|
60
77
|
}
|
|
61
78
|
return;
|
|
62
79
|
}
|
|
63
80
|
if (mode === 'min-anchor') {
|
|
64
|
-
// One
|
|
65
|
-
//
|
|
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`.
|
|
76
|
-
if (supportsAnchorSize) {
|
|
81
|
+
// One floor only. The content floor from mode 'none' deliberately does not also apply
|
|
82
|
+
// here, and cannot: it would need this same property. See the decision note.
|
|
83
|
+
if (supportsAnchorSize()) {
|
|
77
84
|
return setStyle({
|
|
78
85
|
element: node,
|
|
79
86
|
styles: [{
|
|
80
87
|
property: 'min-inline-size',
|
|
81
|
-
value: 'anchor-size(
|
|
88
|
+
value: 'anchor-size(self-inline)'
|
|
82
89
|
}]
|
|
83
90
|
});
|
|
84
91
|
}
|
|
@@ -88,18 +95,20 @@ export function useWidthFromAnchor({
|
|
|
88
95
|
element: node,
|
|
89
96
|
styles: [{
|
|
90
97
|
property: 'min-inline-size',
|
|
91
|
-
value: `${
|
|
98
|
+
value: `${getFallbackAnchorInlineSize({
|
|
99
|
+
anchor,
|
|
100
|
+
popover: node
|
|
101
|
+
})}px`
|
|
92
102
|
}]
|
|
93
103
|
});
|
|
94
104
|
}
|
|
95
105
|
|
|
96
|
-
//
|
|
106
|
+
// Nothing to measure, so no floor.
|
|
97
107
|
return;
|
|
98
108
|
}
|
|
99
109
|
|
|
100
|
-
//
|
|
101
|
-
// `@atlaskit/popper`
|
|
102
|
-
// this floor with `min-inline-size: 0`.
|
|
110
|
+
// Makes a too-narrow span overflow and drive `position-try-fallbacks` instead of wrapping.
|
|
111
|
+
// `@atlaskit/popper` resets this exact property to `0` for its fit-to-viewport mode.
|
|
103
112
|
return setStyle({
|
|
104
113
|
element: node,
|
|
105
114
|
styles: [{
|
|
@@ -13,6 +13,19 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t =
|
|
|
13
13
|
* position.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* A viewport-relative position, in pixels. Physical rather than logical
|
|
18
|
+
* because it is written straight to `top` / `left`.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The cross-axis shift with its length already resolved to pixels.
|
|
23
|
+
*
|
|
24
|
+
* Mirrors `TCrossAxisShiftOffset`, whose `value` is an unresolved CSS length
|
|
25
|
+
* string. Only this module needs the pixel form, because only this module does
|
|
26
|
+
* the arithmetic itself.
|
|
27
|
+
*/
|
|
28
|
+
|
|
16
29
|
/**
|
|
17
30
|
* Computes the base {top, left} position based on the primary edge,
|
|
18
31
|
* flipping to the opposite side when there is not enough space.
|
|
@@ -7,10 +7,15 @@ import { toCssLengthString } from './resolve-css-length';
|
|
|
7
7
|
*/
|
|
8
8
|
var DEFAULT_GAP = "var(--ds-space-100, 8px)";
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Which way along the cross axis a shift moves the popover. `'forwards'` is
|
|
12
|
+
* always toward the cross-axis END and `'backwards'` toward the START, for
|
|
13
|
+
* every `align` value. See `notes/decisions/placement-offset.md`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
10
16
|
/**
|
|
11
17
|
* Cross-axis shift. `value` is a CSS length string (consumer numbers are
|
|
12
|
-
* normalized to `${n}px`).
|
|
13
|
-
* `'backwards'` (toward start).
|
|
18
|
+
* normalized to `${n}px`).
|
|
14
19
|
*/
|
|
15
20
|
|
|
16
21
|
/**
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single inline style declaration.
|
|
3
|
+
*
|
|
4
|
+
* `TProperty` narrows the property name for callers that write a known, closed
|
|
5
|
+
* set of properties. Narrowing it lets a record keyed on those same names be
|
|
6
|
+
* checked for exhaustiveness at compile time, rather than silently resolving to
|
|
7
|
+
* `undefined` for a name that was never mapped.
|
|
8
|
+
*/
|
|
9
|
+
|
|
1
10
|
/**
|
|
2
11
|
* Sets inline styles on an element and returns a cleanup function that
|
|
3
12
|
* restores the prior inline values (so we do not stomp consumer styles).
|
|
@@ -76,7 +76,11 @@ var supportsAnchorPositioning = once(function () {
|
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
|
-
*
|
|
79
|
+
* Every logical margin property this module can write: one per axis, per side.
|
|
80
|
+
*
|
|
81
|
+
* Deriving the union from `TPlacementAxis` and `TPlacementEdge` rather than
|
|
82
|
+
* writing the four names out keeps it in step with the placement model, and
|
|
83
|
+
* makes `CROSS_AXIS_SHIFT_CUSTOM_PROPERTY` below exhaustive by construction.
|
|
80
84
|
*/
|
|
81
85
|
|
|
82
86
|
/**
|
|
@@ -86,6 +90,11 @@ var supportsAnchorPositioning = once(function () {
|
|
|
86
90
|
* reads them yet, and the shift no longer depends on them: the antisymmetric
|
|
87
91
|
* margins written by `crossAxisShiftMargins` survive a cross-axis slide on
|
|
88
92
|
* their own.
|
|
93
|
+
*
|
|
94
|
+
* Keyed on `TLogicalMarginProperty` and not on `string`, so a margin property
|
|
95
|
+
* that has no custom property (or a custom property whose margin no longer
|
|
96
|
+
* exists) is a compile error rather than an `undefined` lookup that reaches
|
|
97
|
+
* `setStyle` as `{ property: undefined, value }`.
|
|
89
98
|
*/
|
|
90
99
|
var CROSS_AXIS_SHIFT_CUSTOM_PROPERTY = {
|
|
91
100
|
'margin-inline-start': '--ds-cross-axis-shift-margin-start',
|
|
@@ -112,34 +121,12 @@ function edgeMargin(_ref) {
|
|
|
112
121
|
offset = _ref.offset;
|
|
113
122
|
var axis = placement.axis,
|
|
114
123
|
edge = placement.edge;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
property: 'margin-block-start',
|
|
119
|
-
value: value
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
if (axis === 'block' && edge === 'start') {
|
|
123
|
-
return {
|
|
124
|
-
property: 'margin-block-end',
|
|
125
|
-
value: value
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
if (axis === 'inline' && edge === 'end') {
|
|
129
|
-
return {
|
|
130
|
-
property: 'margin-inline-start',
|
|
131
|
-
value: value
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
if (axis === 'inline' && edge === 'start') {
|
|
135
|
-
return {
|
|
136
|
-
property: 'margin-inline-end',
|
|
137
|
-
value: value
|
|
138
|
-
};
|
|
139
|
-
}
|
|
124
|
+
// The gap sits on the side FACING the anchor, which is always the side
|
|
125
|
+
// opposite the placement edge.
|
|
126
|
+
var anchorFacingEdge = edge === 'end' ? 'start' : 'end';
|
|
140
127
|
return {
|
|
141
|
-
property:
|
|
142
|
-
value:
|
|
128
|
+
property: "margin-".concat(axis, "-").concat(anchorFacingEdge),
|
|
129
|
+
value: offset
|
|
143
130
|
};
|
|
144
131
|
}
|
|
145
132
|
|
|
@@ -305,10 +292,7 @@ export function useAnchorPosition(_ref3) {
|
|
|
305
292
|
}, {
|
|
306
293
|
property: 'inset',
|
|
307
294
|
value: 'auto'
|
|
308
|
-
}, {
|
|
309
|
-
property: gap.property,
|
|
310
|
-
value: gap.value
|
|
311
|
-
}].concat(_toConsumableArray(crossAxisShift), _toConsumableArray(ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES), _toConsumableArray(crossAxisShift.map(function (_ref4) {
|
|
295
|
+
}, gap].concat(_toConsumableArray(crossAxisShift), _toConsumableArray(ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES), _toConsumableArray(crossAxisShift.map(function (_ref4) {
|
|
312
296
|
var property = _ref4.property,
|
|
313
297
|
value = _ref4.value;
|
|
314
298
|
return {
|
|
@@ -1,83 +1,89 @@
|
|
|
1
1
|
import { useLayoutEffect } from 'react';
|
|
2
|
+
import once from '@atlaskit/ds-lib/once';
|
|
2
3
|
import { setStyle } from './set-style';
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Probes anchor SIZING, separately from `anchor-name` (see
|
|
7
|
+
* `supportsAnchorPositioning`): some browsers have positioning but not sizing.
|
|
8
|
+
* `once()` for SSR safety.
|
|
9
|
+
*/
|
|
10
|
+
var supportsAnchorSize = once(function () {
|
|
11
|
+
if (typeof window === 'undefined' || typeof CSS === 'undefined' || typeof CSS.supports !== 'function') {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return CSS.supports('min-inline-size', 'anchor-size(self-inline)');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The JS fallback's stand-in for `anchor-size(self-inline)`. `offsetWidth` / `offsetHeight` are
|
|
19
|
+
* physical, so the axis is picked from the popover's `writing-mode`. Anything not horizontal is
|
|
20
|
+
* treated as vertical, and an unknown or empty value falls back to `offsetWidth`.
|
|
21
|
+
*/
|
|
22
|
+
function getFallbackAnchorInlineSize(_ref) {
|
|
23
|
+
var anchor = _ref.anchor,
|
|
24
|
+
popover = _ref.popover;
|
|
25
|
+
// `String()` because not every test environment implements `writing-mode`.
|
|
26
|
+
var writingMode = String(getComputedStyle(popover).writingMode);
|
|
27
|
+
var isVertical = writingMode.startsWith('vertical') || writingMode.startsWith('sideways');
|
|
28
|
+
return isVertical ? anchor.offsetHeight : anchor.offsetWidth;
|
|
29
|
+
}
|
|
30
|
+
|
|
4
31
|
/**
|
|
5
32
|
* Sets the width of a popover element relative to its anchor element.
|
|
6
33
|
*
|
|
7
|
-
* - `'match-anchor'` → `
|
|
8
|
-
* - `'min-anchor'` → `min-inline-size: anchor-size(
|
|
9
|
-
*
|
|
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)
|
|
14
|
-
*
|
|
15
|
-
* Uses CSS `anchor-size(width)` when the browser supports it, falling back to a
|
|
16
|
-
* one-off measurement of `anchor.offsetWidth`.
|
|
34
|
+
* - `'match-anchor'` → `inline-size: anchor-size(self-inline)`, exact
|
|
35
|
+
* - `'min-anchor'` → `min-inline-size: anchor-size(self-inline)`, floor only
|
|
36
|
+
* - `'none'` → `min-inline-size: max-content`, content floor (default)
|
|
17
37
|
*
|
|
18
|
-
*
|
|
38
|
+
* `self-inline` is the inline axis of the element USING the function (the popover), never the
|
|
39
|
+
* anchor, so it matches the axis `inline-size` constrains. See
|
|
40
|
+
* `notes/decisions/width-from-anchor-floors.md`.
|
|
19
41
|
*/
|
|
20
|
-
export function useWidthFromAnchor(
|
|
21
|
-
var mode =
|
|
22
|
-
popoverRef =
|
|
23
|
-
anchorRef =
|
|
24
|
-
isOpen =
|
|
42
|
+
export function useWidthFromAnchor(_ref2) {
|
|
43
|
+
var mode = _ref2.mode,
|
|
44
|
+
popoverRef = _ref2.popoverRef,
|
|
45
|
+
anchorRef = _ref2.anchorRef,
|
|
46
|
+
isOpen = _ref2.isOpen;
|
|
25
47
|
useLayoutEffect(function () {
|
|
26
48
|
var node = popoverRef.current;
|
|
27
49
|
if (!node) {
|
|
28
50
|
return;
|
|
29
51
|
}
|
|
30
|
-
|
|
31
|
-
// Feature-detect `anchor-size()` specifically (not just `anchor-name`)
|
|
32
|
-
// so the JS fallback fires correctly in browsers that support anchor
|
|
33
|
-
// positioning but not anchor sizing.
|
|
34
|
-
var supportsAnchorSize = typeof CSS !== 'undefined' && typeof CSS.supports === 'function' && CSS.supports('width', 'anchor-size(width)');
|
|
35
52
|
if (mode === 'match-anchor') {
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
// explicit width constraint, allowing the popover to grow past the anchor's width.
|
|
39
|
-
if (supportsAnchorSize) {
|
|
53
|
+
// No floor here: it would let the popover grow past the anchor.
|
|
54
|
+
if (supportsAnchorSize()) {
|
|
40
55
|
return setStyle({
|
|
41
56
|
element: node,
|
|
42
57
|
styles: [{
|
|
43
|
-
property: '
|
|
44
|
-
value: 'anchor-size(
|
|
58
|
+
property: 'inline-size',
|
|
59
|
+
value: 'anchor-size(self-inline)'
|
|
45
60
|
}]
|
|
46
61
|
});
|
|
47
62
|
}
|
|
48
|
-
|
|
49
|
-
// JS fallback: one-off read of the anchor's width.
|
|
50
63
|
var anchor = anchorRef.current;
|
|
51
64
|
if (anchor) {
|
|
52
65
|
return setStyle({
|
|
53
66
|
element: node,
|
|
54
67
|
styles: [{
|
|
55
|
-
property: '
|
|
56
|
-
value: "".concat(
|
|
68
|
+
property: 'inline-size',
|
|
69
|
+
value: "".concat(getFallbackAnchorInlineSize({
|
|
70
|
+
anchor: anchor,
|
|
71
|
+
popover: node
|
|
72
|
+
}), "px")
|
|
57
73
|
}]
|
|
58
74
|
});
|
|
59
75
|
}
|
|
60
76
|
return;
|
|
61
77
|
}
|
|
62
78
|
if (mode === 'min-anchor') {
|
|
63
|
-
// One
|
|
64
|
-
//
|
|
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`.
|
|
75
|
-
if (supportsAnchorSize) {
|
|
79
|
+
// One floor only. The content floor from mode 'none' deliberately does not also apply
|
|
80
|
+
// here, and cannot: it would need this same property. See the decision note.
|
|
81
|
+
if (supportsAnchorSize()) {
|
|
76
82
|
return setStyle({
|
|
77
83
|
element: node,
|
|
78
84
|
styles: [{
|
|
79
85
|
property: 'min-inline-size',
|
|
80
|
-
value: 'anchor-size(
|
|
86
|
+
value: 'anchor-size(self-inline)'
|
|
81
87
|
}]
|
|
82
88
|
});
|
|
83
89
|
}
|
|
@@ -87,18 +93,20 @@ export function useWidthFromAnchor(_ref) {
|
|
|
87
93
|
element: node,
|
|
88
94
|
styles: [{
|
|
89
95
|
property: 'min-inline-size',
|
|
90
|
-
value: "".concat(
|
|
96
|
+
value: "".concat(getFallbackAnchorInlineSize({
|
|
97
|
+
anchor: _anchor,
|
|
98
|
+
popover: node
|
|
99
|
+
}), "px")
|
|
91
100
|
}]
|
|
92
101
|
});
|
|
93
102
|
}
|
|
94
103
|
|
|
95
|
-
//
|
|
104
|
+
// Nothing to measure, so no floor.
|
|
96
105
|
return;
|
|
97
106
|
}
|
|
98
107
|
|
|
99
|
-
//
|
|
100
|
-
// `@atlaskit/popper`
|
|
101
|
-
// this floor with `min-inline-size: 0`.
|
|
108
|
+
// Makes a too-narrow span overflow and drive `position-try-fallbacks` instead of wrapping.
|
|
109
|
+
// `@atlaskit/popper` resets this exact property to `0` for its fit-to-viewport mode.
|
|
102
110
|
return setStyle({
|
|
103
111
|
element: node,
|
|
104
112
|
styles: [{
|
|
@@ -1,4 +1,34 @@
|
|
|
1
|
-
import { type TPlacement } from './resolve-placement';
|
|
1
|
+
import { type TCrossAxisShiftDirection, type TPlacement } from './resolve-placement';
|
|
2
|
+
/**
|
|
3
|
+
* JS fallback positioning math. Used only when the browser does not
|
|
4
|
+
* support CSS Anchor Positioning (less than 6% of users).
|
|
5
|
+
*
|
|
6
|
+
* Consumer-supplied `offset.gap` and `offset.crossAxisShift` are
|
|
7
|
+
* honored: the caller (`useAnchorPosition`) resolves CSS length strings
|
|
8
|
+
* (tokens, calc, var, etc) to pixels via a DOM probe before passing them
|
|
9
|
+
* in. The caller also keeps the popover hidden (`opacity: 0`) until the
|
|
10
|
+
* first measurement completes, so the popover never paints at the wrong
|
|
11
|
+
* position.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* A viewport-relative position, in pixels. Physical rather than logical
|
|
15
|
+
* because it is written straight to `top` / `left`.
|
|
16
|
+
*/
|
|
17
|
+
type TPosition = {
|
|
18
|
+
top: number;
|
|
19
|
+
left: number;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* The cross-axis shift with its length already resolved to pixels.
|
|
23
|
+
*
|
|
24
|
+
* Mirrors `TCrossAxisShiftOffset`, whose `value` is an unresolved CSS length
|
|
25
|
+
* string. Only this module needs the pixel form, because only this module does
|
|
26
|
+
* the arithmetic itself.
|
|
27
|
+
*/
|
|
28
|
+
type TResolvedCrossAxisShift = {
|
|
29
|
+
value: number;
|
|
30
|
+
direction: TCrossAxisShiftDirection;
|
|
31
|
+
};
|
|
2
32
|
/**
|
|
3
33
|
* Computes popover position from trigger rect and viewport, flipping to
|
|
4
34
|
* the side with more space when needed. Re-runs on scroll/resize.
|
|
@@ -16,11 +46,6 @@ export declare function computeFallbackPosition({ triggerRect, popoverEl, placem
|
|
|
16
46
|
height: number;
|
|
17
47
|
};
|
|
18
48
|
gap: number;
|
|
19
|
-
crossAxisShift:
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
};
|
|
23
|
-
}): {
|
|
24
|
-
top: number;
|
|
25
|
-
left: number;
|
|
26
|
-
};
|
|
49
|
+
crossAxisShift: TResolvedCrossAxisShift;
|
|
50
|
+
}): TPosition;
|
|
51
|
+
export {};
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which way along the cross axis a shift moves the popover. `'forwards'` is
|
|
3
|
+
* always toward the cross-axis END and `'backwards'` toward the START, for
|
|
4
|
+
* every `align` value. See `notes/decisions/placement-offset.md`.
|
|
5
|
+
*/
|
|
6
|
+
export type TCrossAxisShiftDirection = 'forwards' | 'backwards';
|
|
1
7
|
/**
|
|
2
8
|
* Cross-axis shift. `value` is a CSS length string (consumer numbers are
|
|
3
|
-
* normalized to `${n}px`).
|
|
4
|
-
* `'backwards'` (toward start).
|
|
9
|
+
* normalized to `${n}px`).
|
|
5
10
|
*/
|
|
6
11
|
export type TCrossAxisShiftOffset = {
|
|
7
12
|
value: string;
|
|
8
|
-
direction:
|
|
13
|
+
direction: TCrossAxisShiftDirection;
|
|
9
14
|
};
|
|
10
15
|
export type TPlacementAxis = 'block' | 'inline';
|
|
11
16
|
export type TPlacementEdge = 'start' | 'end';
|
|
17
|
+
export type TPlacementAlign = 'start' | 'center' | 'end';
|
|
12
18
|
/**
|
|
13
19
|
* Fully-resolved placement (internal). Use `resolvePlacement` for partial input.
|
|
14
20
|
* `offset.gap` and `offset.crossAxisShift.value` are always CSS
|
|
@@ -17,13 +23,10 @@ export type TPlacementEdge = 'start' | 'end';
|
|
|
17
23
|
export type TPlacement = {
|
|
18
24
|
axis: TPlacementAxis;
|
|
19
25
|
edge: TPlacementEdge;
|
|
20
|
-
align:
|
|
26
|
+
align: TPlacementAlign;
|
|
21
27
|
offset: {
|
|
22
28
|
gap: string;
|
|
23
|
-
crossAxisShift:
|
|
24
|
-
value: string;
|
|
25
|
-
direction: 'forwards' | 'backwards';
|
|
26
|
-
};
|
|
29
|
+
crossAxisShift: TCrossAxisShiftOffset;
|
|
27
30
|
};
|
|
28
31
|
};
|
|
29
32
|
/**
|
|
@@ -36,14 +39,14 @@ export type TPlacement = {
|
|
|
36
39
|
* - `crossAxisShift` - nudge along the trigger edge (perpendicular to the placement axis)
|
|
37
40
|
*/
|
|
38
41
|
export type TPlacementOptions = {
|
|
39
|
-
axis?:
|
|
40
|
-
edge?:
|
|
41
|
-
align?:
|
|
42
|
+
axis?: TPlacementAxis;
|
|
43
|
+
edge?: TPlacementEdge;
|
|
44
|
+
align?: TPlacementAlign;
|
|
42
45
|
offset?: {
|
|
43
46
|
gap?: number | string;
|
|
44
47
|
crossAxisShift?: {
|
|
45
48
|
value?: number | string;
|
|
46
|
-
direction?:
|
|
49
|
+
direction?: TCrossAxisShiftDirection;
|
|
47
50
|
};
|
|
48
51
|
};
|
|
49
52
|
};
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single inline style declaration.
|
|
3
|
+
*
|
|
4
|
+
* `TProperty` narrows the property name for callers that write a known, closed
|
|
5
|
+
* set of properties. Narrowing it lets a record keyed on those same names be
|
|
6
|
+
* checked for exhaustiveness at compile time, rather than silently resolving to
|
|
7
|
+
* `undefined` for a name that was never mapped.
|
|
8
|
+
*/
|
|
9
|
+
export type TStyleDeclaration<TProperty extends string = string> = {
|
|
10
|
+
property: TProperty;
|
|
11
|
+
value: string;
|
|
12
|
+
};
|
|
1
13
|
/**
|
|
2
14
|
* Sets inline styles on an element and returns a cleanup function that
|
|
3
15
|
* restores the prior inline values (so we do not stomp consumer styles).
|
|
4
16
|
*/
|
|
5
17
|
export declare function setStyle({ element, styles, }: {
|
|
6
18
|
element: HTMLElement;
|
|
7
|
-
styles:
|
|
8
|
-
property: string;
|
|
9
|
-
value: string;
|
|
10
|
-
}>;
|
|
19
|
+
styles: TStyleDeclaration[];
|
|
11
20
|
}): () => void;
|
|
@@ -3,18 +3,13 @@ import type { TWidthFromAnchorMode } from '../popover/types';
|
|
|
3
3
|
/**
|
|
4
4
|
* Sets the width of a popover element relative to its anchor element.
|
|
5
5
|
*
|
|
6
|
-
* - `'match-anchor'` → `
|
|
7
|
-
* - `'min-anchor'` → `min-inline-size: anchor-size(
|
|
8
|
-
*
|
|
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)
|
|
6
|
+
* - `'match-anchor'` → `inline-size: anchor-size(self-inline)`, exact
|
|
7
|
+
* - `'min-anchor'` → `min-inline-size: anchor-size(self-inline)`, floor only
|
|
8
|
+
* - `'none'` → `min-inline-size: max-content`, content floor (default)
|
|
13
9
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* Designed to be composed alongside `useAnchorPosition` for top-layer popovers.
|
|
10
|
+
* `self-inline` is the inline axis of the element USING the function (the popover), never the
|
|
11
|
+
* anchor, so it matches the axis `inline-size` constrains. See
|
|
12
|
+
* `notes/decisions/width-from-anchor-floors.md`.
|
|
18
13
|
*/
|
|
19
14
|
export declare function useWidthFromAnchor({ mode, popoverRef, anchorRef, isOpen, }: {
|
|
20
15
|
mode: TWidthFromAnchorMode;
|
|
@@ -166,12 +166,12 @@ export type TPopoverProps = TPopoverBaseProps & TAriaRoleRequired & ({
|
|
|
166
166
|
*
|
|
167
167
|
* - `'none'` (default): popover sizes to its content, ignoring the anchor width.
|
|
168
168
|
* - `'match-anchor'`: popover matches the anchor element's width exactly
|
|
169
|
-
* via CSS `anchor-size(
|
|
170
|
-
* of `anchorRef.current.offsetWidth` when CSS
|
|
171
|
-
* is not supported.
|
|
169
|
+
* via CSS `inline-size: anchor-size(self-inline)`. Falls back to a
|
|
170
|
+
* one-off measurement of `anchorRef.current.offsetWidth` when CSS
|
|
171
|
+
* Anchor Positioning is not supported.
|
|
172
172
|
* - `'min-anchor'`: popover is at least as wide as the anchor, but
|
|
173
173
|
* can grow wider if content requires it. Uses
|
|
174
|
-
* `min-inline-size: anchor-size(
|
|
174
|
+
* `min-inline-size: anchor-size(self-inline)`. Falls back to a one-off
|
|
175
175
|
* measurement of the anchor's width.
|
|
176
176
|
*/
|
|
177
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.4",
|
|
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",
|