@atlaskit/top-layer 4.4.5 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/README.md +13 -4
- package/dist/cjs/dialog/dialog-content.js +2 -0
- package/dist/cjs/entry-points/use-popover-id.js +5 -10
- package/dist/cjs/internal/anchor-positioning/apply-anchor-positioning.js +86 -0
- package/dist/cjs/internal/anchor-positioning/cross-axis-shift-margins.js +34 -0
- package/dist/cjs/internal/anchor-positioning/edge-margin.js +21 -0
- package/dist/cjs/internal/anchor-positioning/fit-margins.js +77 -0
- package/dist/cjs/internal/anchor-positioning/get-existing-anchor-name.js +18 -0
- package/dist/cjs/internal/{placement-to-position-area.js → anchor-positioning/placement-to-position-area.js} +11 -11
- package/dist/cjs/internal/anchor-positioning/placement-to-try-fallbacks.js +95 -0
- package/dist/cjs/internal/anchored-popover-size.js +345 -0
- package/dist/cjs/internal/get-aria-for-trigger.js +7 -13
- package/dist/cjs/internal/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
- package/dist/cjs/internal/javascript-fallback/apply-javascript-fallback-positioning.js +297 -0
- package/dist/cjs/internal/javascript-fallback/resolve-css-length-to-pixels.js +90 -0
- package/dist/cjs/internal/javascript-fallback/warn-fallback-failure.js +19 -0
- package/dist/cjs/internal/resolve-placement.js +14 -6
- package/dist/cjs/internal/supports-anchor-positioning.js +17 -0
- package/dist/cjs/internal/supports-anchor-size.js +17 -0
- package/dist/cjs/internal/supports-css-declaration.js +25 -0
- package/dist/cjs/internal/surface-reset.js +5 -0
- package/dist/cjs/internal/use-anchored-popover-at-point.js +124 -0
- package/dist/cjs/internal/use-anchored-popover.js +277 -0
- package/dist/cjs/popover/popover.compiled.css +4 -0
- package/dist/cjs/popover/popover.js +11 -4
- package/dist/cjs/testing/polyfill.js +6 -7
- package/dist/es2019/dialog/dialog-content.js +2 -0
- package/dist/es2019/entry-points/use-popover-id.js +6 -10
- package/dist/es2019/internal/anchor-positioning/apply-anchor-positioning.js +83 -0
- package/dist/es2019/internal/anchor-positioning/cross-axis-shift-margins.js +29 -0
- package/dist/es2019/internal/anchor-positioning/edge-margin.js +18 -0
- package/dist/es2019/internal/anchor-positioning/fit-margins.js +69 -0
- package/dist/es2019/internal/anchor-positioning/get-existing-anchor-name.js +12 -0
- package/dist/es2019/internal/{placement-to-position-area.js → anchor-positioning/placement-to-position-area.js} +9 -9
- package/dist/es2019/internal/anchor-positioning/placement-to-try-fallbacks.js +94 -0
- package/dist/es2019/internal/anchored-popover-size.js +343 -0
- package/dist/es2019/internal/get-aria-for-trigger.js +7 -13
- package/dist/es2019/internal/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
- package/dist/es2019/internal/javascript-fallback/apply-javascript-fallback-positioning.js +291 -0
- package/dist/es2019/internal/javascript-fallback/resolve-css-length-to-pixels.js +87 -0
- package/dist/es2019/internal/javascript-fallback/warn-fallback-failure.js +14 -0
- package/dist/es2019/internal/resolve-placement.js +14 -6
- package/dist/es2019/internal/supports-anchor-positioning.js +12 -0
- package/dist/es2019/internal/supports-anchor-size.js +12 -0
- package/dist/es2019/internal/supports-css-declaration.js +20 -0
- package/dist/es2019/internal/surface-reset.js +1 -0
- package/dist/es2019/internal/use-anchored-popover-at-point.js +116 -0
- package/dist/es2019/internal/use-anchored-popover.js +265 -0
- package/dist/es2019/popover/popover.compiled.css +4 -0
- package/dist/es2019/popover/popover.js +11 -4
- package/dist/es2019/testing/polyfill.js +6 -7
- package/dist/esm/dialog/dialog-content.js +2 -0
- package/dist/esm/entry-points/use-popover-id.js +6 -10
- package/dist/esm/internal/anchor-positioning/apply-anchor-positioning.js +80 -0
- package/dist/esm/internal/anchor-positioning/cross-axis-shift-margins.js +28 -0
- package/dist/esm/internal/anchor-positioning/edge-margin.js +15 -0
- package/dist/esm/internal/anchor-positioning/fit-margins.js +71 -0
- package/dist/esm/internal/anchor-positioning/get-existing-anchor-name.js +12 -0
- package/dist/esm/internal/{placement-to-position-area.js → anchor-positioning/placement-to-position-area.js} +11 -12
- package/dist/esm/internal/anchor-positioning/placement-to-try-fallbacks.js +89 -0
- package/dist/esm/internal/anchored-popover-size.js +338 -0
- package/dist/esm/internal/get-aria-for-trigger.js +7 -13
- package/dist/esm/internal/{anchor-positioning-fallback.js → javascript-fallback/anchor-positioning-fallback.js} +12 -33
- package/dist/esm/internal/javascript-fallback/apply-javascript-fallback-positioning.js +290 -0
- package/dist/esm/internal/javascript-fallback/resolve-css-length-to-pixels.js +85 -0
- package/dist/esm/internal/javascript-fallback/warn-fallback-failure.js +13 -0
- package/dist/esm/internal/resolve-placement.js +14 -6
- package/dist/esm/internal/supports-anchor-positioning.js +12 -0
- package/dist/esm/internal/supports-anchor-size.js +12 -0
- package/dist/esm/internal/supports-css-declaration.js +19 -0
- package/dist/esm/internal/surface-reset.js +1 -0
- package/dist/esm/internal/use-anchored-popover-at-point.js +118 -0
- package/dist/esm/internal/use-anchored-popover.js +271 -0
- package/dist/esm/popover/popover.compiled.css +4 -0
- package/dist/esm/popover/popover.js +11 -4
- package/dist/esm/testing/polyfill.js +6 -7
- package/dist/types/entry-points/placement-map.d.ts +2 -2
- package/dist/types/entry-points/use-popover-id.d.ts +3 -7
- package/dist/types/internal/anchor-positioning/apply-anchor-positioning.d.ts +26 -0
- package/dist/types/internal/anchor-positioning/cross-axis-shift-margins.d.ts +19 -0
- package/dist/types/internal/anchor-positioning/edge-margin.d.ts +10 -0
- package/dist/types/internal/anchor-positioning/fit-margins.d.ts +30 -0
- package/dist/types/internal/anchor-positioning/get-existing-anchor-name.d.ts +8 -0
- package/dist/types/internal/{placement-to-position-area.d.ts → anchor-positioning/placement-to-position-area.d.ts} +10 -6
- package/dist/types/internal/anchor-positioning/placement-to-try-fallbacks.d.ts +30 -0
- package/dist/types/internal/anchored-popover-size.d.ts +126 -0
- package/dist/types/internal/get-aria-for-trigger.d.ts +13 -25
- package/dist/types/internal/javascript-fallback/anchor-positioning-fallback.d.ts +36 -0
- package/dist/types/internal/javascript-fallback/apply-javascript-fallback-positioning.d.ts +15 -0
- package/dist/types/internal/javascript-fallback/resolve-css-length-to-pixels.d.ts +20 -0
- package/dist/types/internal/javascript-fallback/warn-fallback-failure.d.ts +7 -0
- package/dist/types/internal/resolve-placement.d.ts +18 -2
- package/dist/types/internal/supports-anchor-positioning.d.ts +5 -0
- package/dist/types/internal/supports-anchor-size.d.ts +5 -0
- package/dist/types/internal/supports-css-declaration.d.ts +11 -0
- package/dist/types/internal/surface-reset.d.ts +25 -0
- package/dist/types/internal/use-anchored-popover-at-point.d.ts +48 -0
- package/dist/types/internal/use-anchored-popover.d.ts +143 -0
- package/dist/types/popover/index.d.ts +2 -1
- package/dist/types/popover/popover.d.ts +1 -1
- package/dist/types/popover/types.d.ts +15 -17
- package/dist/types/testing/polyfill.d.ts +3 -3
- package/package.json +2 -2
- package/use-anchored-popover/package.json +10 -0
- package/use-anchored-popover-at-point/package.json +10 -0
- package/dist/cjs/internal/placement-to-try-fallbacks.js +0 -108
- package/dist/cjs/internal/resolve-css-length-to-pixels.js +0 -104
- package/dist/cjs/internal/use-anchor-position-at-point.js +0 -129
- package/dist/cjs/internal/use-anchor-position.js +0 -501
- package/dist/cjs/internal/use-width-from-anchor.js +0 -124
- package/dist/es2019/internal/placement-to-try-fallbacks.js +0 -108
- package/dist/es2019/internal/resolve-css-length-to-pixels.js +0 -101
- package/dist/es2019/internal/use-anchor-position-at-point.js +0 -124
- package/dist/es2019/internal/use-anchor-position.js +0 -480
- package/dist/es2019/internal/use-width-from-anchor.js +0 -120
- package/dist/esm/internal/placement-to-try-fallbacks.js +0 -103
- package/dist/esm/internal/resolve-css-length-to-pixels.js +0 -98
- package/dist/esm/internal/use-anchor-position-at-point.js +0 -124
- package/dist/esm/internal/use-anchor-position.js +0 -494
- package/dist/esm/internal/use-width-from-anchor.js +0 -118
- package/dist/types/internal/anchor-positioning-fallback.d.ts +0 -51
- package/dist/types/internal/placement-to-try-fallbacks.d.ts +0 -13
- package/dist/types/internal/resolve-css-length-to-pixels.d.ts +0 -20
- package/dist/types/internal/use-anchor-position-at-point.d.ts +0 -67
- package/dist/types/internal/use-anchor-position.d.ts +0 -87
- package/dist/types/internal/use-width-from-anchor.d.ts +0 -19
- package/use-anchor-position/package.json +0 -10
- package/use-anchor-position-at-point/package.json +0 -10
- package/use-width-from-anchor/package.json +0 -10
|
@@ -1,480 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Anchor positioning for top-layer popovers. See
|
|
3
|
-
* `notes/architecture/positioning.md` for the full model.
|
|
4
|
-
*
|
|
5
|
-
* Two runtime paths: CSS Anchor Positioning (modern browsers) and a JS
|
|
6
|
-
* fallback that measures the trigger and writes `top`/`left`.
|
|
7
|
-
*
|
|
8
|
-
* Both paths honour `offset.gap` and `offset.crossAxisShift`. The
|
|
9
|
-
* CSS path passes the values through as strings; the JS fallback resolves
|
|
10
|
-
* them to pixels via a hidden DOM probe (see `resolveCssLengthToPixels`)
|
|
11
|
-
* so tokens, `calc()`, `var()`, etc all work. The JS fallback hides the
|
|
12
|
-
* popover with `opacity: 0` until the first measurement completes, so it
|
|
13
|
-
* is never painted at the wrong location.
|
|
14
|
-
*/
|
|
15
|
-
import { useId, useLayoutEffect, useMemo } from 'react';
|
|
16
|
-
import { bind } from 'bind-event-listener';
|
|
17
|
-
import rafSchedule from 'raf-schd';
|
|
18
|
-
import once from '@atlaskit/ds-lib/once';
|
|
19
|
-
import { computeFallbackPosition } from './anchor-positioning-fallback';
|
|
20
|
-
import { combine } from './combine';
|
|
21
|
-
import { placementToPositionArea } from './placement-to-position-area';
|
|
22
|
-
import { placementToTryFallbacks } from './placement-to-try-fallbacks';
|
|
23
|
-
import { resolveCssLengthToPixels } from './resolve-css-length-to-pixels';
|
|
24
|
-
import { resolvePlacement } from './resolve-placement';
|
|
25
|
-
import { setStyle } from './set-style';
|
|
26
|
-
|
|
27
|
-
// `resolvePlacement` is intentionally NOT re-exported here - import it directly
|
|
28
|
-
// from `./resolve-placement` so search-and-jump lands at the source of truth.
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Module-scope no-op `ResizeObserver` used in non-DOM jest environments where
|
|
32
|
-
* `ResizeObserver` is `undefined`. Hoisted out of the effect to avoid
|
|
33
|
-
* allocating a new class declaration on every run.
|
|
34
|
-
*/
|
|
35
|
-
const NoopResizeObserver = class {
|
|
36
|
-
observe() {}
|
|
37
|
-
unobserve() {}
|
|
38
|
-
disconnect() {}
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Returns true when the popover is currently open. Wrapped because the
|
|
43
|
-
* `:popover-open` pseudo-class is not implemented in some test environments
|
|
44
|
-
* (e.g. jsdom), where `matches` throws on unknown selectors.
|
|
45
|
-
*/
|
|
46
|
-
function isPopoverOpen(popover) {
|
|
47
|
-
try {
|
|
48
|
-
return popover.matches(':popover-open');
|
|
49
|
-
} catch {
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Detects whether the browser supports CSS Anchor Positioning.
|
|
56
|
-
* Uses `once()` for lazy evaluation: safe for SSR and avoids hydration mismatches.
|
|
57
|
-
*/
|
|
58
|
-
const supportsAnchorPositioning = once(() => {
|
|
59
|
-
if (typeof window === 'undefined' || typeof CSS === 'undefined' || typeof CSS.supports !== 'function') {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
return CSS.supports('anchor-name', '--a');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
/**
|
|
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.
|
|
71
|
-
*/
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* The custom property mirroring each cross-axis shift margin.
|
|
75
|
-
*
|
|
76
|
-
* These are written for the planned named arrow `@position-try` rules. Nothing
|
|
77
|
-
* reads them yet, and the shift no longer depends on them: the antisymmetric
|
|
78
|
-
* margins written by `crossAxisShiftMargins` survive a cross-axis slide on
|
|
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 }`.
|
|
85
|
-
*/
|
|
86
|
-
const CROSS_AXIS_SHIFT_CUSTOM_PROPERTY = {
|
|
87
|
-
'margin-inline-start': '--ds-cross-axis-shift-margin-start',
|
|
88
|
-
'margin-inline-end': '--ds-cross-axis-shift-margin-end',
|
|
89
|
-
'margin-block-start': '--ds-cross-axis-shift-margin-block-start',
|
|
90
|
-
'margin-block-end': '--ds-cross-axis-shift-margin-block-end'
|
|
91
|
-
};
|
|
92
|
-
const ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES = Object.values(CROSS_AXIS_SHIFT_CUSTOM_PROPERTY).map(property => ({
|
|
93
|
-
property,
|
|
94
|
-
value: '0px'
|
|
95
|
-
}));
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Returns the CSS margin declaration that creates a gap between the
|
|
99
|
-
* popover and its trigger on the side facing the anchor.
|
|
100
|
-
*
|
|
101
|
-
* For example, a `block-end` placement (popover below) gets
|
|
102
|
-
* `margin-block-start: 8px` to push it away from the trigger's bottom edge.
|
|
103
|
-
*/
|
|
104
|
-
function edgeMargin({
|
|
105
|
-
placement,
|
|
106
|
-
offset
|
|
107
|
-
}) {
|
|
108
|
-
const {
|
|
109
|
-
axis,
|
|
110
|
-
edge
|
|
111
|
-
} = placement;
|
|
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';
|
|
115
|
-
return {
|
|
116
|
-
property: `margin-${axis}-${anchorFacingEdge}`,
|
|
117
|
-
value: offset
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* **The cross-axis shift margins.**
|
|
123
|
-
*
|
|
124
|
-
* The shift is written antisymmetrically on BOTH cross-axis sides
|
|
125
|
-
* (`start: +value`, `end: -value`) rather than as a single margin on the side
|
|
126
|
-
* picked from `align`. Two separate behaviours require it:
|
|
127
|
-
*
|
|
128
|
-
* 1. **`align: 'center'` is centered with `anchor-center`, which centers the
|
|
129
|
-
* popover's MARGIN box on the anchor.** A single-sided margin widens the
|
|
130
|
-
* margin box, so it displaces the border box by only half its value. An
|
|
131
|
-
* antisymmetric pair leaves the margin box width unchanged (`+value` and
|
|
132
|
-
* `-value` sum to zero) while moving its center by the full value.
|
|
133
|
-
* 2. **`position-try-fallbacks` can slide the popover onto the opposite
|
|
134
|
-
* cross-axis side.** A `<position-area>` fallback keeps the base style's
|
|
135
|
-
* margins, and margin on the un-anchored side has no effect, so a
|
|
136
|
-
* single-sided margin is silently dropped once the browser slides across the
|
|
137
|
-
* cross axis. With both sides written, whichever side ends up anchored moves
|
|
138
|
-
* the popover the same physical direction.
|
|
139
|
-
*
|
|
140
|
-
* Leaving the margin box width unchanged also means the shift does not move
|
|
141
|
-
* the point at which `position-try-fallbacks` decides the popover overflows.
|
|
142
|
-
*
|
|
143
|
-
* A `<try-tactic>` fallback (`flip-block`, `flip-inline`) behaves differently to
|
|
144
|
-
* a `<position-area>` one: it SWAPS the start and end margins rather than
|
|
145
|
-
* keeping them, which is what preserves the gap through a flip. So a flip across
|
|
146
|
-
* the cross axis mirrors the shift along with the rest of the placement, rather
|
|
147
|
-
* than preserving its physical direction. Only the diagonal fallback does that,
|
|
148
|
-
* and only for `align: 'start' | 'end'`. See `placementToTryFallbacks`.
|
|
149
|
-
*
|
|
150
|
-
* A positive value always moves the popover toward the cross-axis END, so
|
|
151
|
-
* `direction: 'forwards'` means the same thing for every `align` value. See
|
|
152
|
-
* `notes/decisions/placement-offset.md`.
|
|
153
|
-
*/
|
|
154
|
-
function crossAxisShiftMargins({
|
|
155
|
-
placement,
|
|
156
|
-
crossAxisShiftCssValue,
|
|
157
|
-
direction
|
|
158
|
-
}) {
|
|
159
|
-
const crossAxis = placement.axis === 'block' ? 'inline' : 'block';
|
|
160
|
-
// Wrap any string in calc() with the sign factor. CSS handles the math,
|
|
161
|
-
// so opaque values such as design tokens negate correctly.
|
|
162
|
-
const negated = `calc(-1 * ${crossAxisShiftCssValue})`;
|
|
163
|
-
const isForwards = direction === 'forwards';
|
|
164
|
-
return [{
|
|
165
|
-
property: `margin-${crossAxis}-start`,
|
|
166
|
-
value: isForwards ? crossAxisShiftCssValue : negated
|
|
167
|
-
}, {
|
|
168
|
-
property: `margin-${crossAxis}-end`,
|
|
169
|
-
value: isForwards ? negated : crossAxisShiftCssValue
|
|
170
|
-
}];
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Returns a referentially-stable, fully-resolved `TPlacement`. The memo is
|
|
175
|
-
* keyed on the resolved primitive fields so a fresh inline `placement` object
|
|
176
|
-
* (or undefined fields vs explicit defaults) does not produce a fresh result.
|
|
177
|
-
*/
|
|
178
|
-
function useStablePlacement(placement) {
|
|
179
|
-
const resolved = resolvePlacement({
|
|
180
|
-
placement
|
|
181
|
-
});
|
|
182
|
-
const axis = resolved.axis;
|
|
183
|
-
const edge = resolved.edge;
|
|
184
|
-
const align = resolved.align;
|
|
185
|
-
const gap = resolved.offset.gap;
|
|
186
|
-
const shiftValue = resolved.offset.crossAxisShift.value;
|
|
187
|
-
const shiftDirection = resolved.offset.crossAxisShift.direction;
|
|
188
|
-
// Rebuild inside the memo from primitive deps so the factory does not
|
|
189
|
-
// close over the outer `resolved` reference and exhaustive-deps can verify.
|
|
190
|
-
return useMemo(() => ({
|
|
191
|
-
axis,
|
|
192
|
-
edge,
|
|
193
|
-
align,
|
|
194
|
-
offset: {
|
|
195
|
-
gap,
|
|
196
|
-
crossAxisShift: {
|
|
197
|
-
value: shiftValue,
|
|
198
|
-
direction: shiftDirection
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}), [axis, edge, align, gap, shiftValue, shiftDirection]);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Hook that positions an element relative to an anchor element using
|
|
206
|
-
* CSS Anchor Positioning (with a JS fallback for older browsers).
|
|
207
|
-
*
|
|
208
|
-
* This hook is the positioning primitive. It has no knowledge of popovers,
|
|
209
|
-
* visibility, or animation. Compose it with `Popover` for anchor-positioned
|
|
210
|
-
* top-layer content.
|
|
211
|
-
*
|
|
212
|
-
* When CSS Anchor Positioning is supported, it sets CSS properties
|
|
213
|
-
* (`anchor-name`, `position-anchor`, `position-area`, `position-try-fallbacks`)
|
|
214
|
-
* directly on the elements via `el.style.setProperty()`.
|
|
215
|
-
*
|
|
216
|
-
* When not supported, it falls back to JavaScript-based positioning using
|
|
217
|
-
* `position: fixed` with measured coordinates, re-running on scroll (capture)
|
|
218
|
-
* and resize events.
|
|
219
|
-
*/
|
|
220
|
-
// eslint-disable-next-line @atlaskit/volt-strict-mode/no-multiple-exports
|
|
221
|
-
export function useAnchorPosition({
|
|
222
|
-
anchorRef,
|
|
223
|
-
popoverRef,
|
|
224
|
-
placement = {},
|
|
225
|
-
forceFallbackPositioning = false,
|
|
226
|
-
isEnabled = true,
|
|
227
|
-
isOpen
|
|
228
|
-
}) {
|
|
229
|
-
const id = useId();
|
|
230
|
-
const stablePlacement = useStablePlacement(placement);
|
|
231
|
-
useLayoutEffect(() => {
|
|
232
|
-
if (!isEnabled) {
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
const trigger = anchorRef.current;
|
|
236
|
-
const popover = popoverRef.current;
|
|
237
|
-
if (!trigger || !popover) {
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
const gapCssValue = stablePlacement.offset.gap;
|
|
241
|
-
const crossAxisShiftCssValue = stablePlacement.offset.crossAxisShift.value;
|
|
242
|
-
const shouldForceFallback = Boolean(forceFallbackPositioning);
|
|
243
|
-
if (supportsAnchorPositioning() && !shouldForceFallback) {
|
|
244
|
-
// Reuse an existing `anchor-name` if the element already has one.
|
|
245
|
-
// Another popover might already be anchored to the old name.
|
|
246
|
-
const anchorName = trigger.style.getPropertyValue('anchor-name') || `--anchor-${id.replace(/:/g, '')}`;
|
|
247
|
-
const gap = edgeMargin({
|
|
248
|
-
placement: stablePlacement,
|
|
249
|
-
offset: gapCssValue
|
|
250
|
-
});
|
|
251
|
-
// Computed once; also mirrored into the
|
|
252
|
-
// `--ds-cross-axis-shift-margin-*` custom properties below.
|
|
253
|
-
const crossAxisShift = crossAxisShiftMargins({
|
|
254
|
-
placement: stablePlacement,
|
|
255
|
-
crossAxisShiftCssValue,
|
|
256
|
-
direction: stablePlacement.offset.crossAxisShift.direction
|
|
257
|
-
});
|
|
258
|
-
const popoverStyles = [{
|
|
259
|
-
property: 'position-anchor',
|
|
260
|
-
value: anchorName
|
|
261
|
-
}, {
|
|
262
|
-
property: 'position-area',
|
|
263
|
-
value: placementToPositionArea({
|
|
264
|
-
placement: stablePlacement
|
|
265
|
-
})
|
|
266
|
-
}, {
|
|
267
|
-
property: 'position-try-fallbacks',
|
|
268
|
-
value: placementToTryFallbacks({
|
|
269
|
-
placement: stablePlacement
|
|
270
|
-
})
|
|
271
|
-
},
|
|
272
|
-
// Reset browser default popover positioning that conflicts
|
|
273
|
-
// with anchor positioning (UA: `inset: 0; margin: auto;`)
|
|
274
|
-
{
|
|
275
|
-
property: 'margin',
|
|
276
|
-
value: '0'
|
|
277
|
-
}, {
|
|
278
|
-
property: 'inset',
|
|
279
|
-
value: 'auto'
|
|
280
|
-
}, gap, ...crossAxisShift,
|
|
281
|
-
// Zero every side first so the two sides that are NOT on the
|
|
282
|
-
// active cross axis do not keep a value from a previous placement.
|
|
283
|
-
...ZEROED_CROSS_AXIS_SHIFT_CUSTOM_PROPERTIES, ...crossAxisShift.map(({
|
|
284
|
-
property,
|
|
285
|
-
value
|
|
286
|
-
}) => ({
|
|
287
|
-
property: CROSS_AXIS_SHIFT_CUSTOM_PROPERTY[property],
|
|
288
|
-
value
|
|
289
|
-
}))];
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* **We are never cleaning up anchor names**
|
|
293
|
-
*
|
|
294
|
-
* _Rationale_
|
|
295
|
-
*
|
|
296
|
-
* - Multiple popovers can share the same anchor element.
|
|
297
|
-
* - Sometimes the `useAnchorPosition` hook and `Popover`
|
|
298
|
-
* are not in the same component.
|
|
299
|
-
* - There can be times when, even with reference counting, we
|
|
300
|
-
* could remove the anchor-name while it is still being used
|
|
301
|
-
* by something else - especially with async react updates
|
|
302
|
-
* which can cause tearing
|
|
303
|
-
*
|
|
304
|
-
* _More_
|
|
305
|
-
*
|
|
306
|
-
* See `notes/decisions/anchor-name-lifetime.md`
|
|
307
|
-
*/
|
|
308
|
-
trigger.style.setProperty('anchor-name', anchorName);
|
|
309
|
-
return setStyle({
|
|
310
|
-
element: popover,
|
|
311
|
-
styles: popoverStyles
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// JS fallback. The popover is already in the top layer via
|
|
316
|
-
// popover="auto", so we only reset UA defaults and set top/left
|
|
317
|
-
// based on measurements.
|
|
318
|
-
const cleanupBaseStyles = setStyle({
|
|
319
|
-
element: popover,
|
|
320
|
-
styles: [{
|
|
321
|
-
property: 'margin',
|
|
322
|
-
value: '0'
|
|
323
|
-
}, {
|
|
324
|
-
property: 'inset',
|
|
325
|
-
value: 'auto'
|
|
326
|
-
}]
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
// The popover can be consumer-owned (`@atlaskit/popper`'s `createPopper`),
|
|
330
|
-
// so restore prior inline values rather than removing ours.
|
|
331
|
-
let undoPosition;
|
|
332
|
-
let undoHide;
|
|
333
|
-
|
|
334
|
-
// `opacity: 0` not `visibility: hidden`: Firefox skips visibility-hidden
|
|
335
|
-
// elements during `<dialog>` initial-focus traversal.
|
|
336
|
-
function hideUntilPositioned() {
|
|
337
|
-
// Keep the first snapshot, else we capture our own `opacity: 0`.
|
|
338
|
-
// (`!popover` is for TS: narrowing does not reach nested functions.)
|
|
339
|
-
if (!popover || undoHide) {
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
undoHide = setStyle({
|
|
343
|
-
element: popover,
|
|
344
|
-
styles: [{
|
|
345
|
-
property: 'opacity',
|
|
346
|
-
value: '0'
|
|
347
|
-
}]
|
|
348
|
-
});
|
|
349
|
-
}
|
|
350
|
-
function reveal() {
|
|
351
|
-
var _undoHide;
|
|
352
|
-
(_undoHide = undoHide) === null || _undoHide === void 0 ? void 0 : _undoHide();
|
|
353
|
-
undoHide = undefined;
|
|
354
|
-
}
|
|
355
|
-
function restorePosition() {
|
|
356
|
-
var _undoPosition;
|
|
357
|
-
(_undoPosition = undoPosition) === null || _undoPosition === void 0 ? void 0 : _undoPosition();
|
|
358
|
-
undoPosition = undefined;
|
|
359
|
-
}
|
|
360
|
-
function update() {
|
|
361
|
-
if (!trigger || !popover) {
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
const triggerRect = trigger.getBoundingClientRect();
|
|
365
|
-
const viewport = {
|
|
366
|
-
width: window.innerWidth,
|
|
367
|
-
height: window.innerHeight
|
|
368
|
-
};
|
|
369
|
-
|
|
370
|
-
// Resolve consumer-supplied CSS length strings (tokens, calc, var,
|
|
371
|
-
// px, rem, etc) to pixels. The probe is mounted INSIDE the popover
|
|
372
|
-
// itself so the resolved length matches the scope a token would
|
|
373
|
-
// resolve against from inside the popover content. (The popover
|
|
374
|
-
// lives in the top layer, not the trigger's DOM tree, so probing
|
|
375
|
-
// at `popover.parentElement` would resolve in a different scope
|
|
376
|
-
// than the consumer's token authoring expects.)
|
|
377
|
-
const gapPx = resolveCssLengthToPixels({
|
|
378
|
-
value: gapCssValue,
|
|
379
|
-
container: popover
|
|
380
|
-
});
|
|
381
|
-
const crossAxisShiftPx = resolveCssLengthToPixels({
|
|
382
|
-
value: crossAxisShiftCssValue,
|
|
383
|
-
container: popover
|
|
384
|
-
});
|
|
385
|
-
const {
|
|
386
|
-
top,
|
|
387
|
-
left
|
|
388
|
-
} = computeFallbackPosition({
|
|
389
|
-
triggerRect,
|
|
390
|
-
popoverEl: popover,
|
|
391
|
-
placement: stablePlacement,
|
|
392
|
-
viewport,
|
|
393
|
-
gap: gapPx,
|
|
394
|
-
crossAxisShift: {
|
|
395
|
-
value: crossAxisShiftPx,
|
|
396
|
-
direction: stablePlacement.offset.crossAxisShift.direction
|
|
397
|
-
}
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
// Restore first so the snapshot stays the consumer's value.
|
|
401
|
-
restorePosition();
|
|
402
|
-
undoPosition = setStyle({
|
|
403
|
-
element: popover,
|
|
404
|
-
styles: [{
|
|
405
|
-
property: 'top',
|
|
406
|
-
value: `${top}px`
|
|
407
|
-
}, {
|
|
408
|
-
property: 'left',
|
|
409
|
-
value: `${left}px`
|
|
410
|
-
}]
|
|
411
|
-
});
|
|
412
|
-
reveal();
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
// Throttle scroll/resize updates to one per animation frame
|
|
416
|
-
const scheduledUpdate = rafSchedule(update);
|
|
417
|
-
|
|
418
|
-
// Wait for the FIRST valid layout before measuring: the popover
|
|
419
|
-
// is `display: none` until `showPopover()`, and some browsers
|
|
420
|
-
// fire `toggle` before layout, so RAF-after-toggle is unreliable.
|
|
421
|
-
// ResizeObserver fires once the browser has real dimensions.
|
|
422
|
-
// Self-disconnects after one valid measurement; ongoing
|
|
423
|
-
// scroll/resize is handled by the window listeners below.
|
|
424
|
-
// `ResizeObserver` is missing in some non-DOM jest environments
|
|
425
|
-
// (e.g. post-office's `node` environment). Fall back to a no-op
|
|
426
|
-
// observer there. The scroll/resize listeners below still keep
|
|
427
|
-
// the popover positioned in the rare case the consumer also
|
|
428
|
-
// polyfilled `showPopover` but not `ResizeObserver`. Real
|
|
429
|
-
// browsers always have it. The `NoopResizeObserver` class is
|
|
430
|
-
// hoisted to module scope (see top of file) so we do not allocate
|
|
431
|
-
// a new constructor on every effect run.
|
|
432
|
-
const ResizeObserverImpl = typeof ResizeObserver !== 'undefined' ? ResizeObserver : NoopResizeObserver;
|
|
433
|
-
const resizeObserver = new ResizeObserverImpl(() => {
|
|
434
|
-
if (popover.offsetWidth > 0 && popover.offsetHeight > 0) {
|
|
435
|
-
update();
|
|
436
|
-
resizeObserver.disconnect();
|
|
437
|
-
}
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
// If the popover is already open by the time this effect runs (for
|
|
441
|
-
// example, the parent `useAnchorPosition` effect runs after the child
|
|
442
|
-
// `Popover` effect that called `showPopover()`), the initial `toggle`
|
|
443
|
-
// event has already fired and our listener below would miss it. Start
|
|
444
|
-
// observing immediately so the first measurement still happens.
|
|
445
|
-
if (isPopoverOpen(popover)) {
|
|
446
|
-
hideUntilPositioned();
|
|
447
|
-
resizeObserver.observe(popover);
|
|
448
|
-
}
|
|
449
|
-
const undoPositioning = combine(cleanupBaseStyles,
|
|
450
|
-
// Each open needs a fresh measurement, so hide and re-observe.
|
|
451
|
-
bind(popover, {
|
|
452
|
-
type: 'toggle',
|
|
453
|
-
listener: event => {
|
|
454
|
-
const toggleEvent = event;
|
|
455
|
-
if (toggleEvent.newState === 'open') {
|
|
456
|
-
hideUntilPositioned();
|
|
457
|
-
resizeObserver.observe(popover);
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
}), () => resizeObserver.disconnect(), bind(window, {
|
|
461
|
-
type: 'scroll',
|
|
462
|
-
listener: scheduledUpdate,
|
|
463
|
-
options: {
|
|
464
|
-
capture: true,
|
|
465
|
-
passive: true
|
|
466
|
-
}
|
|
467
|
-
}), bind(window, {
|
|
468
|
-
type: 'resize',
|
|
469
|
-
listener: scheduledUpdate,
|
|
470
|
-
options: {
|
|
471
|
-
passive: true
|
|
472
|
-
}
|
|
473
|
-
}), () => {
|
|
474
|
-
scheduledUpdate.cancel();
|
|
475
|
-
restorePosition();
|
|
476
|
-
reveal();
|
|
477
|
-
});
|
|
478
|
-
return undoPositioning;
|
|
479
|
-
}, [anchorRef, popoverRef, stablePlacement, forceFallbackPositioning, isEnabled, id, isOpen]);
|
|
480
|
-
}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { useLayoutEffect } from 'react';
|
|
2
|
-
import once from '@atlaskit/ds-lib/once';
|
|
3
|
-
import { setStyle } from './set-style';
|
|
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
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Sets the width of a popover element relative to its anchor element.
|
|
34
|
-
*
|
|
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)
|
|
38
|
-
*
|
|
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`.
|
|
42
|
-
*/
|
|
43
|
-
export function useWidthFromAnchor({
|
|
44
|
-
mode,
|
|
45
|
-
popoverRef,
|
|
46
|
-
anchorRef,
|
|
47
|
-
isOpen
|
|
48
|
-
}) {
|
|
49
|
-
useLayoutEffect(() => {
|
|
50
|
-
const node = popoverRef.current;
|
|
51
|
-
if (!node) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if (mode === 'match-anchor') {
|
|
55
|
-
// No floor here: it would let the popover grow past the anchor.
|
|
56
|
-
if (supportsAnchorSize()) {
|
|
57
|
-
return setStyle({
|
|
58
|
-
element: node,
|
|
59
|
-
styles: [{
|
|
60
|
-
property: 'inline-size',
|
|
61
|
-
value: 'anchor-size(self-inline)'
|
|
62
|
-
}]
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
const anchor = anchorRef.current;
|
|
66
|
-
if (anchor) {
|
|
67
|
-
return setStyle({
|
|
68
|
-
element: node,
|
|
69
|
-
styles: [{
|
|
70
|
-
property: 'inline-size',
|
|
71
|
-
value: `${getFallbackAnchorInlineSize({
|
|
72
|
-
anchor,
|
|
73
|
-
popover: node
|
|
74
|
-
})}px`
|
|
75
|
-
}]
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
if (mode === 'min-anchor') {
|
|
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()) {
|
|
84
|
-
return setStyle({
|
|
85
|
-
element: node,
|
|
86
|
-
styles: [{
|
|
87
|
-
property: 'min-inline-size',
|
|
88
|
-
value: 'anchor-size(self-inline)'
|
|
89
|
-
}]
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
const anchor = anchorRef.current;
|
|
93
|
-
if (anchor) {
|
|
94
|
-
return setStyle({
|
|
95
|
-
element: node,
|
|
96
|
-
styles: [{
|
|
97
|
-
property: 'min-inline-size',
|
|
98
|
-
value: `${getFallbackAnchorInlineSize({
|
|
99
|
-
anchor,
|
|
100
|
-
popover: node
|
|
101
|
-
})}px`
|
|
102
|
-
}]
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Nothing to measure, so no floor.
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
|
|
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.
|
|
112
|
-
return setStyle({
|
|
113
|
-
element: node,
|
|
114
|
-
styles: [{
|
|
115
|
-
property: 'min-inline-size',
|
|
116
|
-
value: 'max-content'
|
|
117
|
-
}]
|
|
118
|
-
});
|
|
119
|
-
}, [mode, popoverRef, anchorRef, isOpen]);
|
|
120
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { resolvePlacement } from './resolve-placement';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A `position-area` primary edge — the placement axis joined to its edge, e.g.
|
|
5
|
-
* `block-end` or `inline-start`. These are the four edges a popover can sit on.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
function slideOnEdge(_ref) {
|
|
9
|
-
var align = _ref.align,
|
|
10
|
-
edgeKey = _ref.edgeKey,
|
|
11
|
-
crossAxis = _ref.crossAxis;
|
|
12
|
-
if (align === 'center') {
|
|
13
|
-
return ["".concat(edgeKey, " span-").concat(crossAxis, "-end"), "".concat(edgeKey, " span-").concat(crossAxis, "-start")];
|
|
14
|
-
}
|
|
15
|
-
// For `align: 'start'` the popover expands toward `end`, so the "near"
|
|
16
|
-
// span is `end` and the "far" span is `start`; `align: 'end'` mirrors
|
|
17
|
-
// this. The bare `edgeKey` (re-centered) sits between them.
|
|
18
|
-
var near = align === 'start' ? 'end' : 'start';
|
|
19
|
-
var far = align === 'start' ? 'start' : 'end';
|
|
20
|
-
return ["".concat(edgeKey, " span-").concat(crossAxis, "-").concat(near), edgeKey, "".concat(edgeKey, " span-").concat(crossAxis, "-").concat(far)];
|
|
21
|
-
}
|
|
22
|
-
function flipDiagonally(_ref2) {
|
|
23
|
-
var axis = _ref2.axis;
|
|
24
|
-
var flipOnMainAxis = axis === 'block' ? 'flip-block' : 'flip-inline';
|
|
25
|
-
var flipToCrossAxis = axis === 'block' ? 'flip-inline' : 'flip-block';
|
|
26
|
-
return "".concat(flipOnMainAxis, " ").concat(flipToCrossAxis);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Returns the `position-try-fallbacks` value for a placement.
|
|
31
|
-
*
|
|
32
|
-
* @example
|
|
33
|
-
* // Centered on the block-end edge (the default placement):
|
|
34
|
-
* placementToTryFallbacks({ placement: { axis: 'block', edge: 'end', align: 'center' } });
|
|
35
|
-
* // => 'block-end span-inline-end, block-end span-inline-start, flip-block,
|
|
36
|
-
* // block-start span-inline-end, block-start span-inline-start'
|
|
37
|
-
*/
|
|
38
|
-
export function placementToTryFallbacks(_ref3) {
|
|
39
|
-
var placement = _ref3.placement;
|
|
40
|
-
/**
|
|
41
|
-
* **Build the fallback list**
|
|
42
|
-
*
|
|
43
|
-
* Each entry is a complete alternative position.
|
|
44
|
-
* The browser tries them _in order_ and uses the _first_ one that fits.
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
var resolved = resolvePlacement({
|
|
48
|
-
placement: placement
|
|
49
|
-
});
|
|
50
|
-
var axis = resolved.axis,
|
|
51
|
-
edge = resolved.edge,
|
|
52
|
-
align = resolved.align;
|
|
53
|
-
var crossAxis = axis === 'block' ? 'inline' : 'block';
|
|
54
|
-
var flippedEdge = edge === 'start' ? 'end' : 'start';
|
|
55
|
-
var sameEdgeArea = "".concat(axis, "-").concat(edge);
|
|
56
|
-
var oppositeEdgeArea = "".concat(axis, "-").concat(flippedEdge);
|
|
57
|
-
var flipOnMainAxis = axis === 'block' ? 'flip-block' : 'flip-inline';
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* **Fallback algorithm**
|
|
61
|
-
*
|
|
62
|
-
* We have two things we can do to keep something visible:
|
|
63
|
-
*
|
|
64
|
-
* 1. _Slide_ along edges
|
|
65
|
-
* 2. _Flip_ to another edge
|
|
66
|
-
*
|
|
67
|
-
* _Centered popover_
|
|
68
|
-
*
|
|
69
|
-
* Desired: Sits in middle of main edge
|
|
70
|
-
*
|
|
71
|
-
* Fallbacks:
|
|
72
|
-
* 1: _Slide_ along main edge on the cross axis
|
|
73
|
-
* 2: _Flip_ on main axis to opposite edge (centered)
|
|
74
|
-
* 3: _Slide_ along opposite edge on the cross axis
|
|
75
|
-
*
|
|
76
|
-
* _Start or end aligned popover_
|
|
77
|
-
*
|
|
78
|
-
* Desired: Sits at start / end of main edge
|
|
79
|
-
*
|
|
80
|
-
* Fallbacks:
|
|
81
|
-
* 1: _Slide_ along main edge on the cross axis
|
|
82
|
-
* 2: _Flip_ on main axis to opposite edge
|
|
83
|
-
* 3: _Flip_ on the diagonal (flip main axis + flip cross axis) into the
|
|
84
|
-
* opposite corner. For start / end alignment, this is the clean escape
|
|
85
|
-
* when the popover would otherwise land offscreen on both axes (ie in a
|
|
86
|
-
* corner). Rule 4 gets the job done, but a slide drops the gap
|
|
87
|
-
* as named position-area values don't carry margins).
|
|
88
|
-
* The diagonal flip keeps the gap and the alignment.
|
|
89
|
-
* 4: _Slide_ along opposite edge on the cross axis
|
|
90
|
-
*/
|
|
91
|
-
|
|
92
|
-
return [slideOnEdge({
|
|
93
|
-
align: align,
|
|
94
|
-
edgeKey: sameEdgeArea,
|
|
95
|
-
crossAxis: crossAxis
|
|
96
|
-
}), flipOnMainAxis, align !== 'center' ? flipDiagonally({
|
|
97
|
-
axis: axis
|
|
98
|
-
}) : null, slideOnEdge({
|
|
99
|
-
align: align,
|
|
100
|
-
edgeKey: oppositeEdgeArea,
|
|
101
|
-
crossAxis: crossAxis
|
|
102
|
-
})].filter(Boolean).flat().join(', ');
|
|
103
|
-
}
|