@atlaskit/popper 8.0.1 → 8.2.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 +31 -1
- package/dist/cjs/index.js +4 -3
- package/dist/cjs/internal/anchor-context.js +17 -0
- package/dist/cjs/internal/anchor-setter-context.js +17 -0
- package/dist/cjs/internal/read-viewport.js +31 -0
- package/dist/cjs/internal/rect-point-for-placement.js +93 -0
- package/dist/cjs/internal/set-style.js +43 -0
- package/dist/cjs/internal/use-anchor-state.js +23 -0
- package/dist/cjs/internal/use-fit-viewport-max-size.js +155 -0
- package/dist/cjs/internal/use-manager-anchor-setter.js +16 -0
- package/dist/cjs/internal/use-manager-anchor.js +16 -0
- package/dist/cjs/internal/use-reference-visibility.js +164 -0
- package/dist/cjs/manager.js +37 -0
- package/dist/cjs/popper-top-layer.js +286 -0
- package/dist/cjs/popper.js +48 -12
- package/dist/cjs/reference.js +44 -0
- package/dist/es2019/index.js +6 -1
- package/dist/es2019/internal/anchor-context.js +12 -0
- package/dist/es2019/internal/anchor-setter-context.js +9 -0
- package/dist/es2019/internal/read-viewport.js +25 -0
- package/dist/es2019/internal/rect-point-for-placement.js +91 -0
- package/dist/es2019/internal/set-style.js +39 -0
- package/dist/es2019/internal/use-anchor-state.js +12 -0
- package/dist/es2019/internal/use-fit-viewport-max-size.js +151 -0
- package/dist/es2019/internal/use-manager-anchor-setter.js +11 -0
- package/dist/es2019/internal/use-manager-anchor.js +11 -0
- package/dist/es2019/internal/use-reference-visibility.js +146 -0
- package/dist/es2019/manager.js +33 -0
- package/dist/es2019/popper-top-layer.js +267 -0
- package/dist/es2019/popper.js +37 -0
- package/dist/es2019/reference.js +35 -0
- package/dist/esm/index.js +6 -1
- package/dist/esm/internal/anchor-context.js +12 -0
- package/dist/esm/internal/anchor-setter-context.js +11 -0
- package/dist/esm/internal/read-viewport.js +25 -0
- package/dist/esm/internal/rect-point-for-placement.js +87 -0
- package/dist/esm/internal/set-style.js +37 -0
- package/dist/esm/internal/use-anchor-state.js +16 -0
- package/dist/esm/internal/use-fit-viewport-max-size.js +149 -0
- package/dist/esm/internal/use-manager-anchor-setter.js +11 -0
- package/dist/esm/internal/use-manager-anchor.js +11 -0
- package/dist/esm/internal/use-reference-visibility.js +157 -0
- package/dist/esm/manager.js +31 -0
- package/dist/esm/popper-top-layer.js +277 -0
- package/dist/esm/popper.js +48 -12
- package/dist/esm/reference.js +36 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/internal/anchor-context.d.ts +11 -0
- package/dist/types/internal/anchor-setter-context.d.ts +11 -0
- package/dist/types/internal/read-viewport.d.ts +12 -0
- package/dist/types/internal/rect-point-for-placement.d.ts +19 -0
- package/dist/types/internal/set-style.d.ts +14 -0
- package/dist/types/internal/use-anchor-state.d.ts +10 -0
- package/dist/types/internal/use-fit-viewport-max-size.d.ts +53 -0
- package/dist/types/internal/use-manager-anchor-setter.d.ts +7 -0
- package/dist/types/internal/use-manager-anchor.d.ts +6 -0
- package/dist/types/internal/use-reference-visibility.d.ts +32 -0
- package/dist/types/manager.d.ts +13 -0
- package/dist/types/popper-top-layer.d.ts +20 -0
- package/dist/types/reference.d.ts +25 -0
- package/package.json +20 -9
- package/popper.docs.tsx +1 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { Reference as ReactPopperReference } from 'react-popper';
|
|
3
|
+
import mergeRefs from '@atlaskit/ds-lib/merge-refs';
|
|
4
|
+
import { useManagerAnchorSetter } from './internal/use-manager-anchor-setter';
|
|
5
|
+
|
|
6
|
+
// `react-popper`'s `Reference` exposes a loosely-typed callback ref so
|
|
7
|
+
// consumers can attach it to any host element. We mirror that shape
|
|
8
|
+
// instead of locking the children ref to `HTMLElement`, which would
|
|
9
|
+
// break legacy consumers that pass it to `<button>` / `<div>` directly.
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- mirroring react-popper's public ref shape
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Wraps `react-popper`'s `<Reference>` so the anchor element it captures
|
|
14
|
+
* is also published through `@atlaskit/popper`'s own bridge context.
|
|
15
|
+
* The bridge lets the FF-on top-layer adapter discover the anchor even
|
|
16
|
+
* when `react-popper`'s CJS and ESM builds resolve to different context
|
|
17
|
+
* instances (Jest vs production bundlers).
|
|
18
|
+
*
|
|
19
|
+
* The bridge + consumer refs are composed into a single stable callback and
|
|
20
|
+
* passed to `react-popper` as `innerRef`, so the (stable) ref `react-popper`
|
|
21
|
+
* hands to `children` never changes identity. Composing inline instead would
|
|
22
|
+
* produce a new ref callback every render, making React detach/reattach the
|
|
23
|
+
* anchor each commit — firing `publishAnchor(null)` then `publishAnchor(el)`
|
|
24
|
+
* and churning the `Manager`.
|
|
25
|
+
*/
|
|
26
|
+
export function Reference({
|
|
27
|
+
children,
|
|
28
|
+
innerRef
|
|
29
|
+
}) {
|
|
30
|
+
const publishAnchor = useManagerAnchorSetter();
|
|
31
|
+
const composedRef = useMemo(() => mergeRefs([publishAnchor, innerRef]), [publishAnchor, innerRef]);
|
|
32
|
+
return /*#__PURE__*/React.createElement(ReactPopperReference, {
|
|
33
|
+
innerRef: composedRef
|
|
34
|
+
}, children);
|
|
35
|
+
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
export { Popper, placements } from './popper';
|
|
2
|
-
|
|
2
|
+
// `Manager` and `Reference` are re-exported through our own wrappers so
|
|
3
|
+
// the anchor element captured by `<Reference>` reaches `<Popper>` via a
|
|
4
|
+
// single shared context instance, sidestepping the dual CJS/ESM context
|
|
5
|
+
// duplication shipped by `react-popper`.
|
|
6
|
+
export { Manager } from './manager';
|
|
7
|
+
export { Reference } from './reference';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Module-private context used to bridge the anchor element from our
|
|
5
|
+
* `<Reference>` wrapper to `<Popper>`. Deliberately separate from
|
|
6
|
+
* `react-popper`'s own `ManagerReferenceNodeContext`: `react-popper`
|
|
7
|
+
* ships dual CJS and ESM builds, each with its own
|
|
8
|
+
* `React.createContext()` instance, and bundlers and Jest can resolve
|
|
9
|
+
* different builds. Bridging through a context we control guarantees a
|
|
10
|
+
* single shared instance across every consumer of `@atlaskit/popper`.
|
|
11
|
+
*/
|
|
12
|
+
export var AnchorContext = /*#__PURE__*/createContext(null);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createContext } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Module-private context that exposes the anchor setter to descendant
|
|
4
|
+
* `<Reference>` instances inside the same `<Manager>` subtree. The
|
|
5
|
+
* setter forwards the captured element into `AnchorContext` so
|
|
6
|
+
* descendant `<Popper>` instances can discover the anchor without
|
|
7
|
+
* reaching into `react-popper`'s internal context.
|
|
8
|
+
*/
|
|
9
|
+
export var AnchorSetterContext = /*#__PURE__*/createContext(function () {
|
|
10
|
+
return undefined;
|
|
11
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the current viewport size in CSS pixels, preferring the visual
|
|
3
|
+
* viewport (which accounts for pinch-zoom on touch devices) over the
|
|
4
|
+
* layout viewport. Returns `{ width: 0, height: 0 }` in non-DOM
|
|
5
|
+
* environments so callers can use the result unconditionally.
|
|
6
|
+
*/
|
|
7
|
+
export function readViewport() {
|
|
8
|
+
if (typeof window === 'undefined') {
|
|
9
|
+
return {
|
|
10
|
+
width: 0,
|
|
11
|
+
height: 0
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
var visual = window.visualViewport;
|
|
15
|
+
if (visual) {
|
|
16
|
+
return {
|
|
17
|
+
width: visual.width,
|
|
18
|
+
height: visual.height
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
width: window.innerWidth,
|
|
23
|
+
height: window.innerHeight
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reduces an anchor rect to the single viewport point that, wrapped in a
|
|
3
|
+
* zero-size synthetic anchor, produces the same popover position
|
|
4
|
+
* `useAnchorPosition` would for the given placement. This works because
|
|
5
|
+
* `useAnchorPosition` only reads the edge / alignment corner of the rect,
|
|
6
|
+
* so a coincident zero-size point is geometrically equivalent.
|
|
7
|
+
*
|
|
8
|
+
* RTL is resolved here so the downstream synthetic anchor only sees physical
|
|
9
|
+
* coordinates. Defaults mirror `getPlacement` in
|
|
10
|
+
* `@atlaskit/top-layer/placement-map` (`axis: 'block'`, `edge: 'end'`,
|
|
11
|
+
* `align: 'center'`).
|
|
12
|
+
*/
|
|
13
|
+
export function rectPointForPlacement(_ref) {
|
|
14
|
+
var _placement$axis, _placement$edge, _placement$align;
|
|
15
|
+
var rect = _ref.rect,
|
|
16
|
+
placement = _ref.placement,
|
|
17
|
+
isRtl = _ref.isRtl;
|
|
18
|
+
var axis = (_placement$axis = placement.axis) !== null && _placement$axis !== void 0 ? _placement$axis : 'block';
|
|
19
|
+
var edge = (_placement$edge = placement.edge) !== null && _placement$edge !== void 0 ? _placement$edge : 'end';
|
|
20
|
+
var align = (_placement$align = placement.align) !== null && _placement$align !== void 0 ? _placement$align : 'center';
|
|
21
|
+
if (axis === 'block') {
|
|
22
|
+
// Popover sits above (`edge: 'start'`) or below (`edge: 'end'`)
|
|
23
|
+
// the anchor. Hug the matching horizontal edge; align along x.
|
|
24
|
+
var _y = edge === 'start' ? rect.top : rect.bottom;
|
|
25
|
+
var _x = horizontalForAlign({
|
|
26
|
+
rect: rect,
|
|
27
|
+
align: align,
|
|
28
|
+
isRtl: isRtl
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
x: _x,
|
|
32
|
+
y: _y
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// `axis: 'inline'`: popover sits inline-start or inline-end of the
|
|
37
|
+
// anchor. Hug the matching vertical edge; align along y.
|
|
38
|
+
var x = inlineEdgeX({
|
|
39
|
+
rect: rect,
|
|
40
|
+
edge: edge,
|
|
41
|
+
isRtl: isRtl
|
|
42
|
+
});
|
|
43
|
+
var y = verticalForAlign({
|
|
44
|
+
rect: rect,
|
|
45
|
+
align: align
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
x: x,
|
|
49
|
+
y: y
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function horizontalForAlign(_ref2) {
|
|
53
|
+
var rect = _ref2.rect,
|
|
54
|
+
align = _ref2.align,
|
|
55
|
+
isRtl = _ref2.isRtl;
|
|
56
|
+
if (align === 'center') {
|
|
57
|
+
return rect.left + rect.width / 2;
|
|
58
|
+
}
|
|
59
|
+
// `start` / `end` are logical. In LTR, `start` is the left edge.
|
|
60
|
+
// In RTL, `start` is the right edge.
|
|
61
|
+
var isStartLeft = !isRtl;
|
|
62
|
+
if (align === 'start') {
|
|
63
|
+
return isStartLeft ? rect.left : rect.right;
|
|
64
|
+
}
|
|
65
|
+
return isStartLeft ? rect.right : rect.left;
|
|
66
|
+
}
|
|
67
|
+
function verticalForAlign(_ref3) {
|
|
68
|
+
var rect = _ref3.rect,
|
|
69
|
+
align = _ref3.align;
|
|
70
|
+
if (align === 'center') {
|
|
71
|
+
return rect.top + rect.height / 2;
|
|
72
|
+
}
|
|
73
|
+
if (align === 'start') {
|
|
74
|
+
return rect.top;
|
|
75
|
+
}
|
|
76
|
+
return rect.bottom;
|
|
77
|
+
}
|
|
78
|
+
function inlineEdgeX(_ref4) {
|
|
79
|
+
var rect = _ref4.rect,
|
|
80
|
+
edge = _ref4.edge,
|
|
81
|
+
isRtl = _ref4.isRtl;
|
|
82
|
+
var isStartLeft = !isRtl;
|
|
83
|
+
if (edge === 'start') {
|
|
84
|
+
return isStartLeft ? rect.left : rect.right;
|
|
85
|
+
}
|
|
86
|
+
return isStartLeft ? rect.right : rect.left;
|
|
87
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sets inline styles on an element and returns a cleanup function that
|
|
3
|
+
* restores the prior inline values (so we do not stomp consumer styles).
|
|
4
|
+
*
|
|
5
|
+
* Copied from `@atlaskit/top-layer`'s internal `setStyle` (not exported there
|
|
6
|
+
* yet); inline here until top-layer exposes it via a subpath export.
|
|
7
|
+
*/
|
|
8
|
+
export function setStyle(_ref) {
|
|
9
|
+
var element = _ref.element,
|
|
10
|
+
styles = _ref.styles;
|
|
11
|
+
// Snapshot the prior inline value (NOT the computed style - we only want
|
|
12
|
+
// to restore values that the consumer/our previous run inlined). An empty
|
|
13
|
+
// string means "no inline value", in which case cleanup uses removeProperty.
|
|
14
|
+
var priorValues = styles.map(function (_ref2) {
|
|
15
|
+
var property = _ref2.property;
|
|
16
|
+
return {
|
|
17
|
+
property: property,
|
|
18
|
+
value: element.style.getPropertyValue(property)
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
styles.forEach(function (_ref3) {
|
|
22
|
+
var property = _ref3.property,
|
|
23
|
+
value = _ref3.value;
|
|
24
|
+
element.style.setProperty(property, value);
|
|
25
|
+
});
|
|
26
|
+
return function cleanup() {
|
|
27
|
+
priorValues.forEach(function (_ref4) {
|
|
28
|
+
var property = _ref4.property,
|
|
29
|
+
value = _ref4.value;
|
|
30
|
+
if (value === '') {
|
|
31
|
+
element.style.removeProperty(property);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
element.style.setProperty(property, value);
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the local state used by `<Manager>` to publish the anchor
|
|
5
|
+
* captured by `<Reference>` to descendant `<Popper>` instances.
|
|
6
|
+
*/
|
|
7
|
+
export function useAnchorState() {
|
|
8
|
+
var _useState = useState(null),
|
|
9
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
10
|
+
anchor = _useState2[0],
|
|
11
|
+
setAnchor = _useState2[1];
|
|
12
|
+
return {
|
|
13
|
+
anchor: anchor,
|
|
14
|
+
setAnchor: setAnchor
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
|
|
2
|
+
import { useLayoutEffect } from 'react';
|
|
3
|
+
import { setStyle } from './set-style';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Applies the `shouldFitViewport` size caps directly to the
|
|
7
|
+
* `position-area`-anchored popover host. Pure CSS — the browser owns every
|
|
8
|
+
* value, so there is no measurement, no scroll/resize listeners, and no
|
|
9
|
+
* `--ds-popper-anchor-*` custom properties.
|
|
10
|
+
*
|
|
11
|
+
* **Primary (placement) axis** is capped to `calc(100% - 5px - gap)`. Because
|
|
12
|
+
* the host carries `position-area`, its containing block *is* the position-area
|
|
13
|
+
* cell — the region between the anchor edge and the viewport edge — so `100%`
|
|
14
|
+
* resolves to that distance. The popover is pushed `gap` into the cell by its
|
|
15
|
+
* offset margin (`margin-*`, set by `useAnchorPosition`), so the gap is
|
|
16
|
+
* subtracted as well to keep the legacy `viewportPadding = 5` on the far edge.
|
|
17
|
+
* The cap is also more correct than measuring the requested placement: the cell
|
|
18
|
+
* follows whichever side `position-try-fallbacks` actually flips to, so it
|
|
19
|
+
* tracks the flip automatically.
|
|
20
|
+
*
|
|
21
|
+
* **Cross axis** is capped to `calc(100dvw|dvh - 10px)` (viewport, legacy
|
|
22
|
+
* `2 * viewportPadding`). `display: flex` (plus a `min-*-size: 0` reset on the
|
|
23
|
+
* child) lets an oversized child shrink and reflow to the cap. The host stays
|
|
24
|
+
* `overflow: visible` so it never clips the consumer surface's `box-shadow`;
|
|
25
|
+
* scrolling content that cannot reflow is the consumer surface's own
|
|
26
|
+
* responsibility (it owns `overflow`), matching legacy `react-popper`, which
|
|
27
|
+
* applied the cap to the consumer's own element.
|
|
28
|
+
*
|
|
29
|
+
* `min-inline-size` is reset to `0`. `useWidthFromAnchor({ mode: 'none' })`
|
|
30
|
+
* floors the host at `max-content` so a too-narrow span overflows and drives
|
|
31
|
+
* `position-try-fallbacks` rather than wrapping. That floor is the opposite of
|
|
32
|
+
* fitting: when it exceeds a cap, CSS min/max resolution lets the min win and
|
|
33
|
+
* the host overflows the viewport. In fit mode the caps must win, so the floor
|
|
34
|
+
* is neutralised here; `setStyle` restores the prior inline value on cleanup.
|
|
35
|
+
*
|
|
36
|
+
* Verified in Chromium: `max-block-size: calc(100% - …)` on a `position: fixed`
|
|
37
|
+
* + `position-area` element resolves `100%` to the cell, not the viewport.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
var CROSS_INLINE_CAP = 'calc(100dvw - 10px)';
|
|
41
|
+
var CROSS_BLOCK_CAP = 'calc(100dvh - 10px)';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Legacy `viewportPadding` (`preventOverflow` padding) the popover kept from
|
|
45
|
+
* every viewport edge under `react-popper`. The caps above already *reserve*
|
|
46
|
+
* this space (`- 5px` on the primary far edge, `- 10px` = `2 * 5px` on the
|
|
47
|
+
* cross axis), but nothing makes the reservation land on the viewport side:
|
|
48
|
+
* the anchor-side gap is a real margin (`useAnchorPosition`'s `edgeMargin`),
|
|
49
|
+
* the viewport side has none, so a capped popover packs flush against the
|
|
50
|
+
* viewport edge it slid/flipped to. Re-applying the padding as a margin on the
|
|
51
|
+
* three non-anchor sides puts the reserved space back on the viewport side.
|
|
52
|
+
* `position-try` flips mirror margins and slides keep them per-side, so the
|
|
53
|
+
* gap tracks whichever edge the browser settles on.
|
|
54
|
+
*/
|
|
55
|
+
var VIEWPORT_PADDING = '5px';
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The popover's three non-anchor margin sides for a given placement axis — the
|
|
59
|
+
* primary *far* side plus both cross sides. The remaining (anchor-facing) side
|
|
60
|
+
* already carries the offset gap from `useAnchorPosition` and is left alone.
|
|
61
|
+
*/
|
|
62
|
+
var NON_ANCHOR_MARGIN_SIDES = {
|
|
63
|
+
top: ['margin-block-start', 'margin-inline-start', 'margin-inline-end'],
|
|
64
|
+
bottom: ['margin-block-end', 'margin-inline-start', 'margin-inline-end'],
|
|
65
|
+
left: ['margin-inline-start', 'margin-block-start', 'margin-block-end'],
|
|
66
|
+
right: ['margin-inline-end', 'margin-block-start', 'margin-block-end']
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param target the `position-area`-anchored popover host
|
|
71
|
+
* @param placementAxis the primary axis derived from the popper placement
|
|
72
|
+
* @param gap the offset distance the popover is pushed from the
|
|
73
|
+
* anchor (CSS length), subtracted from the primary cap
|
|
74
|
+
* @param isEnabled mirrors the consumer's `shouldFitViewport` prop
|
|
75
|
+
* @param isOpen re-applies the caps after the host unmounts/remounts
|
|
76
|
+
* across open cycles (the host is torn down on exit)
|
|
77
|
+
*/
|
|
78
|
+
export function useFitViewportMaxSize(_ref) {
|
|
79
|
+
var target = _ref.target,
|
|
80
|
+
placementAxis = _ref.placementAxis,
|
|
81
|
+
gap = _ref.gap,
|
|
82
|
+
isEnabled = _ref.isEnabled,
|
|
83
|
+
isOpen = _ref.isOpen;
|
|
84
|
+
useLayoutEffect(function () {
|
|
85
|
+
var element = target.current;
|
|
86
|
+
if (!element || !isEnabled) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
var isBlockAxis = placementAxis === 'top' || placementAxis === 'bottom';
|
|
90
|
+
var primaryAxisCap = "calc(100% - 5px - ".concat(gap, ")");
|
|
91
|
+
var cleanupHost = setStyle({
|
|
92
|
+
element: element,
|
|
93
|
+
styles: [{
|
|
94
|
+
property: 'display',
|
|
95
|
+
value: 'flex'
|
|
96
|
+
},
|
|
97
|
+
// Neutralise `useWidthFromAnchor`'s `min-inline-size: max-content`
|
|
98
|
+
// floor so the caps below win and content reflows. `setStyle`
|
|
99
|
+
// restores the floor on cleanup.
|
|
100
|
+
{
|
|
101
|
+
property: 'min-inline-size',
|
|
102
|
+
value: '0'
|
|
103
|
+
}, isBlockAxis ? {
|
|
104
|
+
property: 'max-block-size',
|
|
105
|
+
value: primaryAxisCap
|
|
106
|
+
} : {
|
|
107
|
+
property: 'max-inline-size',
|
|
108
|
+
value: primaryAxisCap
|
|
109
|
+
}, isBlockAxis ? {
|
|
110
|
+
property: 'max-inline-size',
|
|
111
|
+
value: CROSS_INLINE_CAP
|
|
112
|
+
} : {
|
|
113
|
+
property: 'max-block-size',
|
|
114
|
+
value: CROSS_BLOCK_CAP
|
|
115
|
+
}].concat(_toConsumableArray(NON_ANCHOR_MARGIN_SIDES[placementAxis].map(function (property) {
|
|
116
|
+
return {
|
|
117
|
+
property: property,
|
|
118
|
+
value: VIEWPORT_PADDING
|
|
119
|
+
};
|
|
120
|
+
})))
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// The host deliberately stays `overflow: visible`. An ancestor with
|
|
124
|
+
// `overflow: auto` clips a descendant's `box-shadow`, so capping and
|
|
125
|
+
// scrolling on the host would strip the elevation shadow from the
|
|
126
|
+
// consumer surface. Instead the host only constrains size; scrolling
|
|
127
|
+
// oversized content is the consumer surface's own responsibility,
|
|
128
|
+
// matching the contract `@atlaskit/top-layer`'s `PopoverSurface` follows
|
|
129
|
+
// (shadow and `overflow` on one element). For that surface (the host's
|
|
130
|
+
// flex child) to be clamped to the cap and scroll its own content, it
|
|
131
|
+
// must be allowed to shrink below its intrinsic size, so reset its
|
|
132
|
+
// min-size floor.
|
|
133
|
+
var child = element.firstElementChild;
|
|
134
|
+
var cleanupChild = child instanceof HTMLElement ? setStyle({
|
|
135
|
+
element: child,
|
|
136
|
+
styles: [{
|
|
137
|
+
property: 'min-block-size',
|
|
138
|
+
value: '0'
|
|
139
|
+
}, {
|
|
140
|
+
property: 'min-inline-size',
|
|
141
|
+
value: '0'
|
|
142
|
+
}]
|
|
143
|
+
}) : undefined;
|
|
144
|
+
return function () {
|
|
145
|
+
cleanupHost();
|
|
146
|
+
cleanupChild === null || cleanupChild === void 0 || cleanupChild();
|
|
147
|
+
};
|
|
148
|
+
}, [target, placementAxis, gap, isEnabled, isOpen]);
|
|
149
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { AnchorSetterContext } from './anchor-setter-context';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the setter that an ancestor `<Manager>` exposes to publish
|
|
6
|
+
* the anchor element from `<Reference>`. Returns a no-op when there is
|
|
7
|
+
* no surrounding `<Manager>`.
|
|
8
|
+
*/
|
|
9
|
+
export function useManagerAnchorSetter() {
|
|
10
|
+
return useContext(AnchorSetterContext);
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { AnchorContext } from './anchor-context';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns the current anchor element published by an ancestor
|
|
6
|
+
* `<Reference>` via the shared `<Manager>` provider. Returns `null`
|
|
7
|
+
* when there is no surrounding `<Manager>` / `<Reference>` pair.
|
|
8
|
+
*/
|
|
9
|
+
export function useManagerAnchor() {
|
|
10
|
+
return useContext(AnchorContext);
|
|
11
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
2
|
+
import { useCallback, useLayoutEffect, useState } from 'react';
|
|
3
|
+
import { bind, bindAll } from 'bind-event-listener';
|
|
4
|
+
import { readViewport } from './read-viewport';
|
|
5
|
+
var INITIAL = {
|
|
6
|
+
isReferenceHidden: false,
|
|
7
|
+
hasPopperEscaped: false
|
|
8
|
+
};
|
|
9
|
+
function isFullyOutsideViewport(rect, viewport) {
|
|
10
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
if (rect.bottom <= 0) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (rect.right <= 0) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (rect.top >= viewport.height) {
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
if (rect.left >= viewport.width) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
function isClippingOverflow(value) {
|
|
28
|
+
return value === 'auto' || value === 'scroll' || value === 'hidden' || value === 'clip';
|
|
29
|
+
}
|
|
30
|
+
function isClippedByAncestors(element) {
|
|
31
|
+
var rect = element.getBoundingClientRect();
|
|
32
|
+
var parent = element.parentElement;
|
|
33
|
+
while (parent) {
|
|
34
|
+
// Read the longhand axis properties directly. The `overflow` shorthand
|
|
35
|
+
// is only populated when both axes share the same value, so reading it
|
|
36
|
+
// alone misses ancestors that set only `overflow-x` or `overflow-y`.
|
|
37
|
+
var style = window.getComputedStyle(parent);
|
|
38
|
+
var clipsHorizontally = isClippingOverflow(style.overflowX);
|
|
39
|
+
var clipsVertically = isClippingOverflow(style.overflowY);
|
|
40
|
+
if (clipsHorizontally || clipsVertically) {
|
|
41
|
+
var parentRect = parent.getBoundingClientRect();
|
|
42
|
+
var horizontallyOutside = rect.right <= parentRect.left || rect.left >= parentRect.right;
|
|
43
|
+
var verticallyOutside = rect.bottom <= parentRect.top || rect.top >= parentRect.bottom;
|
|
44
|
+
if (clipsHorizontally && horizontallyOutside) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (clipsVertically && verticallyOutside) {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
parent = parent.parentElement;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Exists **only** to preserve API parity with the legacy popper.js
|
|
58
|
+
* implementation. The top-layer popper does not natively expose
|
|
59
|
+
* popper.js's `isReferenceHidden` / `hasPopperEscaped` render-prop
|
|
60
|
+
* modifiers, but existing consumers still branch on those values, so
|
|
61
|
+
* this hook reconstructs them from live DOM measurement.
|
|
62
|
+
*
|
|
63
|
+
* Do not reach for this hook in new code. If you find yourself wanting
|
|
64
|
+
* these signals for a new feature, prefer a first-class top-layer or
|
|
65
|
+
* anchor-positioning primitive instead.
|
|
66
|
+
*
|
|
67
|
+
* - `isReferenceHidden`: the anchor's bounding rect is fully outside the
|
|
68
|
+
* visual viewport OR is clipped to zero area by any scrollable
|
|
69
|
+
* ancestor.
|
|
70
|
+
* - `hasPopperEscaped`: the popover surface's rect is fully outside the
|
|
71
|
+
* visual viewport.
|
|
72
|
+
*
|
|
73
|
+
* Measurement runs in a `useLayoutEffect` so the first paint already
|
|
74
|
+
* reflects the correct values, preventing consumers from animating
|
|
75
|
+
* from `opacity: 1` to `opacity: 0` on mount.
|
|
76
|
+
*/
|
|
77
|
+
export function useReferenceVisibility(_ref) {
|
|
78
|
+
var anchor = _ref.anchor,
|
|
79
|
+
popoverRef = _ref.popoverRef;
|
|
80
|
+
var _useState = useState(INITIAL),
|
|
81
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
82
|
+
visibility = _useState2[0],
|
|
83
|
+
setVisibility = _useState2[1];
|
|
84
|
+
var measure = useCallback(function () {
|
|
85
|
+
if (!anchor) {
|
|
86
|
+
setVisibility(function (previous) {
|
|
87
|
+
return previous.isReferenceHidden === false && previous.hasPopperEscaped === false ? previous : INITIAL;
|
|
88
|
+
});
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
var viewport = readViewport();
|
|
92
|
+
var anchorRect = anchor.getBoundingClientRect();
|
|
93
|
+
var isReferenceHidden = isFullyOutsideViewport(anchorRect, viewport) || isClippedByAncestors(anchor);
|
|
94
|
+
var popoverElement = popoverRef.current;
|
|
95
|
+
var hasPopperEscaped = popoverElement ? isFullyOutsideViewport(popoverElement.getBoundingClientRect(), viewport) : false;
|
|
96
|
+
setVisibility(function (previous) {
|
|
97
|
+
if (previous.isReferenceHidden === isReferenceHidden && previous.hasPopperEscaped === hasPopperEscaped) {
|
|
98
|
+
return previous;
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
isReferenceHidden: isReferenceHidden,
|
|
102
|
+
hasPopperEscaped: hasPopperEscaped
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
}, [anchor, popoverRef]);
|
|
106
|
+
useLayoutEffect(function () {
|
|
107
|
+
measure();
|
|
108
|
+
if (typeof window === 'undefined') {
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
var cleanups = [];
|
|
112
|
+
|
|
113
|
+
// Resize observer on the anchor so layout changes update visibility.
|
|
114
|
+
if (anchor && typeof ResizeObserver !== 'undefined') {
|
|
115
|
+
var observer = new ResizeObserver(function () {
|
|
116
|
+
return measure();
|
|
117
|
+
});
|
|
118
|
+
observer.observe(anchor);
|
|
119
|
+
cleanups.push(function () {
|
|
120
|
+
return observer.disconnect();
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Scroll + resize: any scroll event in the document can move the
|
|
125
|
+
// anchor in/out of any scrollable ancestor's viewport.
|
|
126
|
+
cleanups.push(bindAll(window, [{
|
|
127
|
+
type: 'scroll',
|
|
128
|
+
listener: measure,
|
|
129
|
+
options: {
|
|
130
|
+
capture: true,
|
|
131
|
+
passive: true
|
|
132
|
+
}
|
|
133
|
+
}, {
|
|
134
|
+
type: 'resize',
|
|
135
|
+
listener: measure,
|
|
136
|
+
options: {
|
|
137
|
+
passive: true
|
|
138
|
+
}
|
|
139
|
+
}]));
|
|
140
|
+
var visual = window.visualViewport;
|
|
141
|
+
if (visual) {
|
|
142
|
+
cleanups.push(bind(visual, {
|
|
143
|
+
type: 'resize',
|
|
144
|
+
listener: measure,
|
|
145
|
+
options: {
|
|
146
|
+
passive: true
|
|
147
|
+
}
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
return function () {
|
|
151
|
+
cleanups.forEach(function (cleanup) {
|
|
152
|
+
return cleanup();
|
|
153
|
+
});
|
|
154
|
+
};
|
|
155
|
+
}, [anchor, measure]);
|
|
156
|
+
return visibility;
|
|
157
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Manager as ReactPopperManager } from 'react-popper';
|
|
3
|
+
import { AnchorContext } from './internal/anchor-context';
|
|
4
|
+
import { AnchorSetterContext } from './internal/anchor-setter-context';
|
|
5
|
+
import { useAnchorState } from './internal/use-anchor-state';
|
|
6
|
+
|
|
7
|
+
// Derive the wrapper's props from `react-popper`'s own `ManagerProps` so the
|
|
8
|
+
// public surface provably accepts exactly what the underlying `<Manager>`
|
|
9
|
+
// accepts (today just `children`) and stays in sync if that contract changes.
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Wraps `react-popper`'s `<Manager>` so the anchor that `<Reference>`
|
|
13
|
+
* captures is published through `@atlaskit/popper`'s own bridge context.
|
|
14
|
+
* Doing so insulates `<Popper>` consumers from `react-popper`'s dual
|
|
15
|
+
* CJS / ESM builds, which otherwise create two unrelated context
|
|
16
|
+
* instances that prevent the FF-on top-layer adapter from discovering
|
|
17
|
+
* the anchor.
|
|
18
|
+
*/
|
|
19
|
+
export function Manager(_ref) {
|
|
20
|
+
var children = _ref.children;
|
|
21
|
+
// `setAnchor` is the `useState` setter, which React guarantees is stable
|
|
22
|
+
// across renders, so it can be passed straight to the context provider.
|
|
23
|
+
var _useAnchorState = useAnchorState(),
|
|
24
|
+
anchor = _useAnchorState.anchor,
|
|
25
|
+
setAnchor = _useAnchorState.setAnchor;
|
|
26
|
+
return /*#__PURE__*/React.createElement(ReactPopperManager, null, /*#__PURE__*/React.createElement(AnchorSetterContext.Provider, {
|
|
27
|
+
value: setAnchor
|
|
28
|
+
}, /*#__PURE__*/React.createElement(AnchorContext.Provider, {
|
|
29
|
+
value: anchor
|
|
30
|
+
}, children)));
|
|
31
|
+
}
|