@atlaskit/popper 8.0.1 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/index.js +4 -3
  3. package/dist/cjs/internal/anchor-context.js +17 -0
  4. package/dist/cjs/internal/anchor-setter-context.js +17 -0
  5. package/dist/cjs/internal/read-viewport.js +31 -0
  6. package/dist/cjs/internal/rect-point-for-placement.js +93 -0
  7. package/dist/cjs/internal/set-style.js +43 -0
  8. package/dist/cjs/internal/use-anchor-state.js +23 -0
  9. package/dist/cjs/internal/use-fit-viewport-max-size.js +155 -0
  10. package/dist/cjs/internal/use-manager-anchor-setter.js +16 -0
  11. package/dist/cjs/internal/use-manager-anchor.js +16 -0
  12. package/dist/cjs/internal/use-reference-visibility.js +164 -0
  13. package/dist/cjs/manager.js +37 -0
  14. package/dist/cjs/popper-top-layer.js +286 -0
  15. package/dist/cjs/popper.js +48 -12
  16. package/dist/cjs/reference.js +44 -0
  17. package/dist/es2019/index.js +6 -1
  18. package/dist/es2019/internal/anchor-context.js +12 -0
  19. package/dist/es2019/internal/anchor-setter-context.js +9 -0
  20. package/dist/es2019/internal/read-viewport.js +25 -0
  21. package/dist/es2019/internal/rect-point-for-placement.js +91 -0
  22. package/dist/es2019/internal/set-style.js +39 -0
  23. package/dist/es2019/internal/use-anchor-state.js +12 -0
  24. package/dist/es2019/internal/use-fit-viewport-max-size.js +151 -0
  25. package/dist/es2019/internal/use-manager-anchor-setter.js +11 -0
  26. package/dist/es2019/internal/use-manager-anchor.js +11 -0
  27. package/dist/es2019/internal/use-reference-visibility.js +146 -0
  28. package/dist/es2019/manager.js +33 -0
  29. package/dist/es2019/popper-top-layer.js +267 -0
  30. package/dist/es2019/popper.js +37 -0
  31. package/dist/es2019/reference.js +35 -0
  32. package/dist/esm/index.js +6 -1
  33. package/dist/esm/internal/anchor-context.js +12 -0
  34. package/dist/esm/internal/anchor-setter-context.js +11 -0
  35. package/dist/esm/internal/read-viewport.js +25 -0
  36. package/dist/esm/internal/rect-point-for-placement.js +87 -0
  37. package/dist/esm/internal/set-style.js +37 -0
  38. package/dist/esm/internal/use-anchor-state.js +16 -0
  39. package/dist/esm/internal/use-fit-viewport-max-size.js +149 -0
  40. package/dist/esm/internal/use-manager-anchor-setter.js +11 -0
  41. package/dist/esm/internal/use-manager-anchor.js +11 -0
  42. package/dist/esm/internal/use-reference-visibility.js +157 -0
  43. package/dist/esm/manager.js +31 -0
  44. package/dist/esm/popper-top-layer.js +277 -0
  45. package/dist/esm/popper.js +48 -12
  46. package/dist/esm/reference.js +36 -0
  47. package/dist/types/index.d.ts +2 -1
  48. package/dist/types/internal/anchor-context.d.ts +11 -0
  49. package/dist/types/internal/anchor-setter-context.d.ts +11 -0
  50. package/dist/types/internal/read-viewport.d.ts +12 -0
  51. package/dist/types/internal/rect-point-for-placement.d.ts +19 -0
  52. package/dist/types/internal/set-style.d.ts +14 -0
  53. package/dist/types/internal/use-anchor-state.d.ts +10 -0
  54. package/dist/types/internal/use-fit-viewport-max-size.d.ts +53 -0
  55. package/dist/types/internal/use-manager-anchor-setter.d.ts +7 -0
  56. package/dist/types/internal/use-manager-anchor.d.ts +6 -0
  57. package/dist/types/internal/use-reference-visibility.d.ts +32 -0
  58. package/dist/types/manager.d.ts +13 -0
  59. package/dist/types/popper-top-layer.d.ts +20 -0
  60. package/dist/types/reference.d.ts +25 -0
  61. package/package.json +15 -4
  62. 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
- export { Manager, Reference } from 'react-popper';
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
+ }