braid-design-system 33.12.3 → 33.12.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # braid-design-system
2
2
 
3
+ ## 33.12.4
4
+
5
+ ### Patch Changes
6
+
7
+ - **Autosuggest, MenuRenderer, OverflowMenu, TooltipRenderer**: Ensure `TooltipRenderer` is correctly positioned on Android devices ([#1926](https://github.com/seek-oss/braid-design-system/pull/1926))
8
+
9
+ Additionally, adjust `TooltipRenderer` positioning at screen edges, removing extra edge spacing.
10
+ This ensures the tooltip arrow is more closely aligned with the trigger when the trigger is near the edge of the screen.
11
+
3
12
  ## 33.12.3
4
13
 
5
14
  ### Patch Changes
package/dist/index.d.mts CHANGED
@@ -3421,6 +3421,13 @@ interface LoaderProps {
3421
3421
  declare const Loader: ({ size, "aria-label": ariaLabel, delayVisibility, data, ...restProps }: LoaderProps) => react.JSX.Element;
3422
3422
 
3423
3423
  type Placement = 'top' | 'bottom';
3424
+ interface PopoverPlacementData {
3425
+ placement: Placement;
3426
+ arrow?: {
3427
+ x?: number;
3428
+ y?: number;
3429
+ };
3430
+ }
3424
3431
  interface PopoverProps {
3425
3432
  id?: string;
3426
3433
  role: NonNullable<AllHTMLAttributes<HTMLElement>['role'] | false>;
@@ -3433,8 +3440,10 @@ interface PopoverProps {
3433
3440
  modal?: boolean;
3434
3441
  open: boolean;
3435
3442
  onClose?: () => void;
3443
+ onPlacementChange?: (data: PopoverPlacementData) => void;
3436
3444
  triggerRef: RefObject<HTMLElement | null>;
3437
3445
  enterFocusRef?: RefObject<HTMLElement | null>;
3446
+ arrowRef?: RefObject<HTMLElement | null>;
3438
3447
  children: ReactNode;
3439
3448
  }
3440
3449
 
package/dist/index.d.ts CHANGED
@@ -3421,6 +3421,13 @@ interface LoaderProps {
3421
3421
  declare const Loader: ({ size, "aria-label": ariaLabel, delayVisibility, data, ...restProps }: LoaderProps) => react.JSX.Element;
3422
3422
 
3423
3423
  type Placement = 'top' | 'bottom';
3424
+ interface PopoverPlacementData {
3425
+ placement: Placement;
3426
+ arrow?: {
3427
+ x?: number;
3428
+ y?: number;
3429
+ };
3430
+ }
3424
3431
  interface PopoverProps {
3425
3432
  id?: string;
3426
3433
  role: NonNullable<AllHTMLAttributes<HTMLElement>['role'] | false>;
@@ -3433,8 +3440,10 @@ interface PopoverProps {
3433
3440
  modal?: boolean;
3434
3441
  open: boolean;
3435
3442
  onClose?: () => void;
3443
+ onPlacementChange?: (data: PopoverPlacementData) => void;
3436
3444
  triggerRef: RefObject<HTMLElement | null>;
3437
3445
  enterFocusRef?: RefObject<HTMLElement | null>;
3446
+ arrowRef?: RefObject<HTMLElement | null>;
3438
3447
  children: ReactNode;
3439
3448
  }
3440
3449
 
@@ -7,16 +7,15 @@ const lib_hooks_useFallbackId_cjs = require("../../hooks/useFallbackId.cjs");
7
7
  const lib_hooks_useIsomorphicLayoutEffect_cjs = require("../../hooks/useIsomorphicLayoutEffect.cjs");
8
8
  const lib_components_Box_Box_cjs = require("../Box/Box.cjs");
9
9
  const lib_components_private_Popover_Popover_cjs = require("../private/Popover/Popover.cjs");
10
- const lib_components_private_animationTimeout_cjs = require("../private/animationTimeout.cjs");
11
10
  const lib_components_private_defaultTextProps_cjs = require("../private/defaultTextProps.cjs");
12
11
  const lib_components_useSpace_useSpace_cjs = require("../useSpace/useSpace.cjs");
13
12
  const lib_components_useThemeName_useThemeName_cjs = require("../useThemeName/useThemeName.cjs");
14
13
  const lib_components_TooltipRenderer_TooltipRenderer_css_cjs = require("./TooltipRenderer.css.cjs");
15
14
  const _interopDefaultCompat = (e) => e && typeof e === "object" && "default" in e ? e : { default: e };
16
15
  const isMobile__default = /* @__PURE__ */ _interopDefaultCompat(isMobile);
17
- const edgeOffset = "xxsmall";
18
16
  const offsetSpace = "small";
19
17
  const StaticTooltipContext = react.createContext(false);
18
+ const clamp = (min, value, max) => Math.max(min, Math.min(value, max));
20
19
  const TooltipTextDefaultsProvider = ({
21
20
  children
22
21
  }) => {
@@ -31,35 +30,59 @@ const TooltipTextDefaultsProvider = ({
31
30
  );
32
31
  };
33
32
  const TooltipContent = ({
34
- inferredPlacement,
35
- arrowLeftOffset,
33
+ placement,
34
+ arrowPosition,
35
+ arrowRef,
36
36
  children
37
- }) => /* @__PURE__ */ jsxRuntime.jsx(
38
- lib_components_Box_Box_cjs.Box,
39
- {
40
- textAlign: "left",
41
- boxShadow: "large",
42
- background: "neutral",
43
- borderRadius: "large",
44
- padding: "small",
45
- marginX: edgeOffset,
46
- className: [lib_components_TooltipRenderer_TooltipRenderer_css_cjs.maxWidth, lib_components_TooltipRenderer_TooltipRenderer_css_cjs.translateZ0],
47
- children: /* @__PURE__ */ jsxRuntime.jsxs(TooltipTextDefaultsProvider, { children: [
48
- /* @__PURE__ */ jsxRuntime.jsx(lib_components_Box_Box_cjs.Box, { className: lib_components_TooltipRenderer_TooltipRenderer_css_cjs.overflowWrap, zIndex: 1, position: "relative", children }),
49
- /* @__PURE__ */ jsxRuntime.jsx(
50
- lib_components_Box_Box_cjs.Box,
51
- {
52
- position: "fixed",
53
- background: "neutral",
54
- className: lib_components_TooltipRenderer_TooltipRenderer_css_cjs.arrow[inferredPlacement],
55
- style: dynamic.assignInlineVars({
56
- [lib_components_TooltipRenderer_TooltipRenderer_css_cjs.horizontalOffset]: `${arrowLeftOffset}px`
57
- })
58
- }
59
- )
60
- ] })
61
- }
62
- );
37
+ }) => {
38
+ const arrowX = arrowPosition?.x;
39
+ const arrowY = arrowPosition?.y;
40
+ const { space, grid } = lib_components_useSpace_useSpace_cjs.useSpace();
41
+ const edgeOffsetInPx = grid * space.xsmall;
42
+ const arrowWidthOffset = parseFloat(lib_components_TooltipRenderer_TooltipRenderer_css_cjs.constants.arrowSize) * 2;
43
+ const [tooltipWidth, setTooltipWidth] = react.useState(0);
44
+ const tooltipContainerRef = react.useRef(null);
45
+ lib_hooks_useIsomorphicLayoutEffect_cjs.useIsomorphicLayoutEffect(() => {
46
+ if (!tooltipContainerRef.current) {
47
+ return;
48
+ }
49
+ const { width } = tooltipContainerRef.current.getBoundingClientRect();
50
+ setTooltipWidth(width);
51
+ }, [children]);
52
+ const clampedArrowX = arrowX !== void 0 && tooltipWidth > 0 ? clamp(
53
+ edgeOffsetInPx,
54
+ arrowX,
55
+ tooltipWidth - edgeOffsetInPx - arrowWidthOffset
56
+ ) : arrowX;
57
+ return /* @__PURE__ */ jsxRuntime.jsx(
58
+ lib_components_Box_Box_cjs.Box,
59
+ {
60
+ ref: tooltipContainerRef,
61
+ textAlign: "left",
62
+ boxShadow: "large",
63
+ background: "neutral",
64
+ borderRadius: "large",
65
+ padding: "small",
66
+ className: [lib_components_TooltipRenderer_TooltipRenderer_css_cjs.maxWidth, lib_components_TooltipRenderer_TooltipRenderer_css_cjs.translateZ0],
67
+ children: /* @__PURE__ */ jsxRuntime.jsxs(TooltipTextDefaultsProvider, { children: [
68
+ /* @__PURE__ */ jsxRuntime.jsx(lib_components_Box_Box_cjs.Box, { className: lib_components_TooltipRenderer_TooltipRenderer_css_cjs.overflowWrap, zIndex: 1, position: "relative", children }),
69
+ /* @__PURE__ */ jsxRuntime.jsx(
70
+ lib_components_Box_Box_cjs.Box,
71
+ {
72
+ ref: arrowRef,
73
+ position: "fixed",
74
+ background: "neutral",
75
+ className: lib_components_TooltipRenderer_TooltipRenderer_css_cjs.arrow[placement ?? "top"],
76
+ style: dynamic.assignInlineVars({
77
+ [lib_components_TooltipRenderer_TooltipRenderer_css_cjs.arrowX]: clampedArrowX !== void 0 ? `${clampedArrowX}px` : void 0,
78
+ [lib_components_TooltipRenderer_TooltipRenderer_css_cjs.arrowY]: arrowY !== void 0 ? `${arrowY}px` : void 0
79
+ })
80
+ }
81
+ )
82
+ ] })
83
+ }
84
+ );
85
+ };
63
86
  const TooltipRenderer = ({
64
87
  id,
65
88
  tooltip,
@@ -67,16 +90,11 @@ const TooltipRenderer = ({
67
90
  children
68
91
  }) => {
69
92
  const resolvedId = lib_hooks_useFallbackId_cjs.useFallbackId(id);
70
- const tooltipRef = react.useRef(null);
71
93
  const triggerRef = react.useRef(null);
94
+ const arrowRef = react.useRef(null);
72
95
  const [open, setOpen] = react.useState(false);
73
- const [triggerPosition, setTriggerPosition] = react.useState(
74
- void 0
75
- );
76
- const [tooltipPosition, setTooltipPosition] = react.useState(
77
- void 0
78
- );
79
- const { grid, space } = lib_components_useSpace_useSpace_cjs.useSpace();
96
+ const [resolvedPlacement, setResolvedPlacement] = react.useState(placement);
97
+ const [arrowPosition, setArrowPosition] = react.useState();
80
98
  const isStatic = react.useContext(StaticTooltipContext);
81
99
  const isMobileDevice = react.useRef(isMobile__default.default()).current;
82
100
  const onScreen = react.useRef(null);
@@ -136,38 +154,6 @@ const TooltipRenderer = ({
136
154
  }
137
155
  };
138
156
  }, [open, isMobileDevice]);
139
- const handleTooltipPosition = () => {
140
- const setPositions = () => {
141
- if (tooltipRef.current) {
142
- setTooltipPosition(tooltipRef.current.getBoundingClientRect());
143
- }
144
- if (triggerRef.current) {
145
- setTriggerPosition(triggerRef.current.getBoundingClientRect());
146
- }
147
- };
148
- setTimeout(() => {
149
- const frameId = requestAnimationFrame(setPositions);
150
- return () => cancelAnimationFrame(frameId);
151
- }, lib_components_private_animationTimeout_cjs.animationTimeout / 2);
152
- };
153
- lib_hooks_useIsomorphicLayoutEffect_cjs.useIsomorphicLayoutEffect(() => {
154
- if (!showTooltip) {
155
- return;
156
- }
157
- handleTooltipPosition();
158
- window.addEventListener("resize", handleTooltipPosition);
159
- return () => {
160
- window.removeEventListener("resize", handleTooltipPosition);
161
- };
162
- }, [showTooltip]);
163
- let inferredPlacement = placement;
164
- let arrowLeftOffset = 0;
165
- if (tooltipPosition && triggerPosition) {
166
- inferredPlacement = tooltipPosition.top > triggerPosition.top ? "bottom" : "top";
167
- const edgeOffsetInPx = space[edgeOffset] * grid;
168
- const tooltipLeftToTriggerLeft = triggerPosition.left - tooltipPosition.left - edgeOffsetInPx;
169
- arrowLeftOffset = tooltipLeftToTriggerLeft + triggerPosition.width / 2;
170
- }
171
157
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
172
158
  children({
173
159
  triggerProps: {
@@ -183,7 +169,6 @@ const TooltipRenderer = ({
183
169
  {
184
170
  id: resolvedId,
185
171
  role: "tooltip",
186
- ref: tooltipRef,
187
172
  offsetSpace,
188
173
  align: "center",
189
174
  placement,
@@ -192,12 +177,18 @@ const TooltipRenderer = ({
192
177
  modal: false,
193
178
  open: showTooltip,
194
179
  onClose: !isStatic ? () => setOpen(false) : void 0,
180
+ onPlacementChange: ({ placement: newPlacement, arrow }) => {
181
+ setResolvedPlacement(newPlacement);
182
+ setArrowPosition(arrow);
183
+ },
195
184
  triggerRef,
185
+ arrowRef,
196
186
  children: /* @__PURE__ */ jsxRuntime.jsx(
197
187
  TooltipContent,
198
188
  {
199
- inferredPlacement,
200
- arrowLeftOffset,
189
+ placement: resolvedPlacement,
190
+ arrowPosition,
191
+ arrowRef,
201
192
  children: tooltip
202
193
  }
203
194
  )
@@ -2,7 +2,6 @@
2
2
  const fileScope = require("@vanilla-extract/css/fileScope");
3
3
  const css = require("@vanilla-extract/css");
4
4
  const cssUtils = require("@vanilla-extract/css-utils");
5
- const lib_css_atoms_atomicProperties_cjs = require("../../css/atoms/atomicProperties.cjs");
6
5
  const lib_themes_vars_css_cjs = require("../../themes/vars.css.cjs");
7
6
  fileScope.setFileScope("src/lib/components/TooltipRenderer/TooltipRenderer.css.ts", "braid-design-system");
8
7
  const constants = {
@@ -21,11 +20,11 @@ const translateZ0 = css.style({
21
20
  }, "translateZ0");
22
21
  const borderRadius = lib_themes_vars_css_cjs.vars.borderRadius.small;
23
22
  const offset = cssUtils.calc(constants.arrowSize).divide(2).negate().toString();
24
- const horizontalOffset = css.createVar("horizontalOffset");
25
- const arrowEdgePadding = "medium";
23
+ const arrowX = css.createVar("arrowX");
24
+ const arrowY = css.createVar("arrowY");
26
25
  const baseArrow = css.style({
27
- left: `clamp(${[lib_css_atoms_atomicProperties_cjs.space[arrowEdgePadding], horizontalOffset, cssUtils.calc("100%").subtract(lib_css_atoms_atomicProperties_cjs.space[arrowEdgePadding])].join(", ")})`,
28
- transform: "translateX(-50%)",
26
+ left: arrowX,
27
+ top: arrowY,
29
28
  visibility: "hidden",
30
29
  ":before": {
31
30
  visibility: "visible",
@@ -52,7 +51,9 @@ const arrow = css.styleVariants({
52
51
  }, "arrow");
53
52
  fileScope.endFileScope();
54
53
  exports.arrow = arrow;
55
- exports.horizontalOffset = horizontalOffset;
54
+ exports.arrowX = arrowX;
55
+ exports.arrowY = arrowY;
56
+ exports.constants = constants;
56
57
  exports.maxWidth = maxWidth;
57
58
  exports.overflowWrap = overflowWrap;
58
59
  exports.translateZ0 = translateZ0;
@@ -1,7 +1,6 @@
1
1
  import { setFileScope, endFileScope } from "@vanilla-extract/css/fileScope";
2
2
  import { style, styleVariants, createVar } from "@vanilla-extract/css";
3
3
  import { calc } from "@vanilla-extract/css-utils";
4
- import { space } from "../../css/atoms/atomicProperties.mjs";
5
4
  import { vars } from "../../themes/vars.css.mjs";
6
5
  setFileScope("src/lib/components/TooltipRenderer/TooltipRenderer.css.ts", "braid-design-system");
7
6
  const constants = {
@@ -20,11 +19,11 @@ const translateZ0 = style({
20
19
  }, "translateZ0");
21
20
  const borderRadius = vars.borderRadius.small;
22
21
  const offset = calc(constants.arrowSize).divide(2).negate().toString();
23
- const horizontalOffset = createVar("horizontalOffset");
24
- const arrowEdgePadding = "medium";
22
+ const arrowX = createVar("arrowX");
23
+ const arrowY = createVar("arrowY");
25
24
  const baseArrow = style({
26
- left: `clamp(${[space[arrowEdgePadding], horizontalOffset, calc("100%").subtract(space[arrowEdgePadding])].join(", ")})`,
27
- transform: "translateX(-50%)",
25
+ left: arrowX,
26
+ top: arrowY,
28
27
  visibility: "hidden",
29
28
  ":before": {
30
29
  visibility: "visible",
@@ -52,7 +51,9 @@ const arrow = styleVariants({
52
51
  endFileScope();
53
52
  export {
54
53
  arrow,
55
- horizontalOffset,
54
+ arrowX,
55
+ arrowY,
56
+ constants,
56
57
  maxWidth,
57
58
  overflowWrap,
58
59
  translateZ0
@@ -6,14 +6,13 @@ import { useFallbackId } from "../../hooks/useFallbackId.mjs";
6
6
  import { useIsomorphicLayoutEffect } from "../../hooks/useIsomorphicLayoutEffect.mjs";
7
7
  import { Box } from "../Box/Box.mjs";
8
8
  import { Popover } from "../private/Popover/Popover.mjs";
9
- import { animationTimeout } from "../private/animationTimeout.mjs";
10
9
  import { DefaultTextPropsProvider } from "../private/defaultTextProps.mjs";
11
10
  import { useSpace } from "../useSpace/useSpace.mjs";
12
11
  import { useThemeName } from "../useThemeName/useThemeName.mjs";
13
- import { maxWidth, translateZ0, overflowWrap, arrow, horizontalOffset } from "./TooltipRenderer.css.mjs";
14
- const edgeOffset = "xxsmall";
12
+ import { maxWidth, translateZ0, overflowWrap, arrow, arrowY, arrowX, constants } from "./TooltipRenderer.css.mjs";
15
13
  const offsetSpace = "small";
16
14
  const StaticTooltipContext = createContext(false);
15
+ const clamp = (min, value, max) => Math.max(min, Math.min(value, max));
17
16
  const TooltipTextDefaultsProvider = ({
18
17
  children
19
18
  }) => {
@@ -28,35 +27,59 @@ const TooltipTextDefaultsProvider = ({
28
27
  );
29
28
  };
30
29
  const TooltipContent = ({
31
- inferredPlacement,
32
- arrowLeftOffset,
30
+ placement,
31
+ arrowPosition,
32
+ arrowRef,
33
33
  children
34
- }) => /* @__PURE__ */ jsx(
35
- Box,
36
- {
37
- textAlign: "left",
38
- boxShadow: "large",
39
- background: "neutral",
40
- borderRadius: "large",
41
- padding: "small",
42
- marginX: edgeOffset,
43
- className: [maxWidth, translateZ0],
44
- children: /* @__PURE__ */ jsxs(TooltipTextDefaultsProvider, { children: [
45
- /* @__PURE__ */ jsx(Box, { className: overflowWrap, zIndex: 1, position: "relative", children }),
46
- /* @__PURE__ */ jsx(
47
- Box,
48
- {
49
- position: "fixed",
50
- background: "neutral",
51
- className: arrow[inferredPlacement],
52
- style: assignInlineVars({
53
- [horizontalOffset]: `${arrowLeftOffset}px`
54
- })
55
- }
56
- )
57
- ] })
58
- }
59
- );
34
+ }) => {
35
+ const arrowX$1 = arrowPosition?.x;
36
+ const arrowY$1 = arrowPosition?.y;
37
+ const { space, grid } = useSpace();
38
+ const edgeOffsetInPx = grid * space.xsmall;
39
+ const arrowWidthOffset = parseFloat(constants.arrowSize) * 2;
40
+ const [tooltipWidth, setTooltipWidth] = useState(0);
41
+ const tooltipContainerRef = useRef(null);
42
+ useIsomorphicLayoutEffect(() => {
43
+ if (!tooltipContainerRef.current) {
44
+ return;
45
+ }
46
+ const { width } = tooltipContainerRef.current.getBoundingClientRect();
47
+ setTooltipWidth(width);
48
+ }, [children]);
49
+ const clampedArrowX = arrowX$1 !== void 0 && tooltipWidth > 0 ? clamp(
50
+ edgeOffsetInPx,
51
+ arrowX$1,
52
+ tooltipWidth - edgeOffsetInPx - arrowWidthOffset
53
+ ) : arrowX$1;
54
+ return /* @__PURE__ */ jsx(
55
+ Box,
56
+ {
57
+ ref: tooltipContainerRef,
58
+ textAlign: "left",
59
+ boxShadow: "large",
60
+ background: "neutral",
61
+ borderRadius: "large",
62
+ padding: "small",
63
+ className: [maxWidth, translateZ0],
64
+ children: /* @__PURE__ */ jsxs(TooltipTextDefaultsProvider, { children: [
65
+ /* @__PURE__ */ jsx(Box, { className: overflowWrap, zIndex: 1, position: "relative", children }),
66
+ /* @__PURE__ */ jsx(
67
+ Box,
68
+ {
69
+ ref: arrowRef,
70
+ position: "fixed",
71
+ background: "neutral",
72
+ className: arrow[placement ?? "top"],
73
+ style: assignInlineVars({
74
+ [arrowX]: clampedArrowX !== void 0 ? `${clampedArrowX}px` : void 0,
75
+ [arrowY]: arrowY$1 !== void 0 ? `${arrowY$1}px` : void 0
76
+ })
77
+ }
78
+ )
79
+ ] })
80
+ }
81
+ );
82
+ };
60
83
  const TooltipRenderer = ({
61
84
  id,
62
85
  tooltip,
@@ -64,16 +87,11 @@ const TooltipRenderer = ({
64
87
  children
65
88
  }) => {
66
89
  const resolvedId = useFallbackId(id);
67
- const tooltipRef = useRef(null);
68
90
  const triggerRef = useRef(null);
91
+ const arrowRef = useRef(null);
69
92
  const [open, setOpen] = useState(false);
70
- const [triggerPosition, setTriggerPosition] = useState(
71
- void 0
72
- );
73
- const [tooltipPosition, setTooltipPosition] = useState(
74
- void 0
75
- );
76
- const { grid, space } = useSpace();
93
+ const [resolvedPlacement, setResolvedPlacement] = useState(placement);
94
+ const [arrowPosition, setArrowPosition] = useState();
77
95
  const isStatic = useContext(StaticTooltipContext);
78
96
  const isMobileDevice = useRef(isMobile()).current;
79
97
  const onScreen = useRef(null);
@@ -133,38 +151,6 @@ const TooltipRenderer = ({
133
151
  }
134
152
  };
135
153
  }, [open, isMobileDevice]);
136
- const handleTooltipPosition = () => {
137
- const setPositions = () => {
138
- if (tooltipRef.current) {
139
- setTooltipPosition(tooltipRef.current.getBoundingClientRect());
140
- }
141
- if (triggerRef.current) {
142
- setTriggerPosition(triggerRef.current.getBoundingClientRect());
143
- }
144
- };
145
- setTimeout(() => {
146
- const frameId = requestAnimationFrame(setPositions);
147
- return () => cancelAnimationFrame(frameId);
148
- }, animationTimeout / 2);
149
- };
150
- useIsomorphicLayoutEffect(() => {
151
- if (!showTooltip) {
152
- return;
153
- }
154
- handleTooltipPosition();
155
- window.addEventListener("resize", handleTooltipPosition);
156
- return () => {
157
- window.removeEventListener("resize", handleTooltipPosition);
158
- };
159
- }, [showTooltip]);
160
- let inferredPlacement = placement;
161
- let arrowLeftOffset = 0;
162
- if (tooltipPosition && triggerPosition) {
163
- inferredPlacement = tooltipPosition.top > triggerPosition.top ? "bottom" : "top";
164
- const edgeOffsetInPx = space[edgeOffset] * grid;
165
- const tooltipLeftToTriggerLeft = triggerPosition.left - tooltipPosition.left - edgeOffsetInPx;
166
- arrowLeftOffset = tooltipLeftToTriggerLeft + triggerPosition.width / 2;
167
- }
168
154
  return /* @__PURE__ */ jsxs(Fragment, { children: [
169
155
  children({
170
156
  triggerProps: {
@@ -180,7 +166,6 @@ const TooltipRenderer = ({
180
166
  {
181
167
  id: resolvedId,
182
168
  role: "tooltip",
183
- ref: tooltipRef,
184
169
  offsetSpace,
185
170
  align: "center",
186
171
  placement,
@@ -189,12 +174,18 @@ const TooltipRenderer = ({
189
174
  modal: false,
190
175
  open: showTooltip,
191
176
  onClose: !isStatic ? () => setOpen(false) : void 0,
177
+ onPlacementChange: ({ placement: newPlacement, arrow: arrow2 }) => {
178
+ setResolvedPlacement(newPlacement);
179
+ setArrowPosition(arrow2);
180
+ },
192
181
  triggerRef,
182
+ arrowRef,
193
183
  children: /* @__PURE__ */ jsx(
194
184
  TooltipContent,
195
185
  {
196
- inferredPlacement,
197
- arrowLeftOffset,
186
+ placement: resolvedPlacement,
187
+ arrowPosition,
188
+ arrowRef,
198
189
  children: tooltip
199
190
  }
200
191
  )
@@ -1,37 +1,34 @@
1
1
  "use strict";
2
2
  const jsxRuntime = require("react/jsx-runtime");
3
- const dynamic = require("@vanilla-extract/dynamic");
3
+ const reactDom = require("@floating-ui/react-dom");
4
4
  const dedent = require("dedent");
5
5
  const react = require("react");
6
- const lib_hooks_useIsomorphicLayoutEffect_cjs = require("../../../hooks/useIsomorphicLayoutEffect.cjs");
7
6
  const lib_components_Box_Box_cjs = require("../../Box/Box.cjs");
8
7
  const lib_components_BraidPortal_BraidPortal_cjs = require("../../BraidPortal/BraidPortal.cjs");
8
+ const lib_components_useResponsiveValue_useResponsiveValue_cjs = require("../../useResponsiveValue/useResponsiveValue.cjs");
9
9
  const lib_components_useSpace_useSpace_cjs = require("../../useSpace/useSpace.cjs");
10
10
  const lib_components_private_animationTimeout_cjs = require("../animationTimeout.cjs");
11
11
  const lib_components_private_Popover_Popover_css_cjs = require("./Popover.css.cjs");
12
+ const lib_css_atoms_sprinkles_css_cjs = require("../../../css/atoms/sprinkles.css.cjs");
12
13
  const _interopDefaultCompat = (e) => e && typeof e === "object" && "default" in e ? e : { default: e };
13
14
  const dedent__default = /* @__PURE__ */ _interopDefaultCompat(dedent);
14
- const zIndex = "notification";
15
- const getPosition = (element) => {
16
- if (!element) {
17
- return void 0;
15
+ const positionMap = {
16
+ top: {
17
+ left: "top-start",
18
+ center: "top",
19
+ right: "top-end"
20
+ },
21
+ bottom: {
22
+ left: "bottom-start",
23
+ center: "bottom",
24
+ right: "bottom-end"
18
25
  }
19
- const rect = element.getBoundingClientRect();
20
- const { top, bottom, left, right, width } = rect;
21
- const { scrollX: scrollX2, scrollY, innerWidth } = window;
22
- return {
23
- // For `top`, we subtract this from the dynamic viewport height in `Popover.css.ts`
24
- // which can't be accessed from Javascript.
25
- top: top + scrollY,
26
- bottom: bottom + scrollY,
27
- left: left + scrollX2,
28
- right: innerWidth - right - scrollX2,
29
- width
30
- };
31
26
  };
32
- function clamp(min, preferred, max) {
33
- return Math.min(Math.max(preferred, min), max);
27
+ function getFloatingUiPosition(placement, align) {
28
+ return positionMap[placement][align];
34
29
  }
30
+ const zIndex = "notification";
31
+ const PopoverContext = react.createContext(null);
35
32
  const PopoverContent = react.forwardRef(
36
33
  ({
37
34
  id,
@@ -45,18 +42,59 @@ const PopoverContent = react.forwardRef(
45
42
  modal = true,
46
43
  open,
47
44
  onClose,
45
+ onPlacementChange,
48
46
  triggerRef,
49
47
  enterFocusRef,
48
+ arrowRef,
50
49
  children
51
50
  }, forwardedRef) => {
52
- const [triggerPosition, setTriggerPosition] = react.useState(void 0);
53
51
  const ref = react.useRef(null);
54
52
  react.useImperativeHandle(forwardedRef, () => ref.current);
55
- const [horizontalOffset, setHorizontalOffset] = react.useState(0);
56
- const [actualPlacement, setActualPlacement] = react.useState(placement);
57
- const showPopover = open && triggerPosition;
58
- const transitionThresholdInPx = lib_components_useSpace_useSpace_cjs.useSpace().space[lib_components_private_Popover_Popover_css_cjs.transitionThreshold];
59
- const alignmentAnchor = align === "center" ? "left" : align;
53
+ const normalized = lib_css_atoms_sprinkles_css_cjs.normalizeResponsiveValue(offsetSpace);
54
+ const mobile = normalized.mobile ?? "none";
55
+ const tablet = normalized.tablet ?? mobile;
56
+ const desktop = normalized.desktop ?? tablet;
57
+ const wide = normalized.wide ?? desktop;
58
+ const resolvedOffsetSpace = lib_components_useResponsiveValue_useResponsiveValue_cjs.useResponsiveValue()({ mobile, tablet, desktop, wide }) ?? mobile;
59
+ const spaceScale = lib_components_useSpace_useSpace_cjs.useSpace();
60
+ let offsetSpacePx = 0;
61
+ if (resolvedOffsetSpace !== "none") {
62
+ offsetSpacePx = spaceScale.space[resolvedOffsetSpace] * spaceScale.grid;
63
+ }
64
+ const floatingUiRequestedPosition = getFloatingUiPosition(placement, align);
65
+ const middleware = [
66
+ reactDom.offset(offsetSpacePx),
67
+ !lockPlacement && reactDom.flip(),
68
+ width === "full" ? reactDom.size({
69
+ apply({ rects, elements }) {
70
+ Object.assign(elements.floating.style, {
71
+ width: `${rects.reference.width}px`
72
+ });
73
+ }
74
+ }) : reactDom.shift({
75
+ crossAxis: align === "center"
76
+ }),
77
+ arrowRef && reactDom.arrow({ element: arrowRef })
78
+ ].filter(Boolean);
79
+ const {
80
+ refs,
81
+ floatingStyles,
82
+ middlewareData,
83
+ placement: floatingUiEvaluatedPosition,
84
+ isPositioned
85
+ } = reactDom.useFloating({
86
+ placement: floatingUiRequestedPosition,
87
+ middleware,
88
+ whileElementsMounted: reactDom.autoUpdate
89
+ });
90
+ react.useEffect(() => {
91
+ refs.setReference(triggerRef.current);
92
+ }, [triggerRef, refs]);
93
+ react.useEffect(() => {
94
+ if (ref.current) {
95
+ refs.setFloating(ref.current);
96
+ }
97
+ }, [refs]);
60
98
  react.useEffect(() => {
61
99
  const handleKeydown = (event) => {
62
100
  if (event.key === "Escape" || event.key === "Tab") {
@@ -81,9 +119,6 @@ const PopoverContent = react.forwardRef(
81
119
  window.removeEventListener("keydown", handleKeydown);
82
120
  };
83
121
  }, [onClose, triggerRef]);
84
- react.useEffect(() => {
85
- setTriggerPosition(getPosition(triggerRef.current));
86
- }, [triggerRef]);
87
122
  react.useEffect(() => {
88
123
  setTimeout(() => {
89
124
  if (!enterFocusRef) {
@@ -101,95 +136,24 @@ const PopoverContent = react.forwardRef(
101
136
  }
102
137
  }
103
138
  }, lib_components_private_animationTimeout_cjs.animationTimeout);
104
- }, [open, enterFocusRef, triggerRef]);
139
+ }, [open, enterFocusRef]);
140
+ const resolvedPlacement = floatingUiEvaluatedPosition?.startsWith("top") ? "top" : "bottom";
105
141
  react.useEffect(() => {
106
- const handleResize = () => {
107
- setTriggerPosition(getPosition(triggerRef.current));
108
- };
109
- window.addEventListener("resize", handleResize);
110
- const resizeObserver = new ResizeObserver(handleResize);
111
- resizeObserver.observe(document.body);
112
- return () => {
113
- window.removeEventListener("resize", handleResize);
114
- resizeObserver.disconnect();
115
- };
116
- }, [triggerRef]);
117
- const handlePlacement = () => {
118
- const popoverBoundingRect = ref?.current?.getBoundingClientRect();
119
- if (!popoverBoundingRect) {
120
- return;
121
- }
122
- const triggerBoundingRect = triggerRef.current?.getBoundingClientRect();
123
- if (!triggerBoundingRect) {
124
- return;
125
- }
126
- const { height: popoverHeight } = popoverBoundingRect;
127
- const heightRequired = popoverHeight + transitionThresholdInPx;
128
- const fitsAbove = triggerBoundingRect.top >= heightRequired;
129
- const fitsBelow = window.innerHeight - triggerBoundingRect.bottom >= heightRequired;
130
- if (!fitsAbove && fitsBelow) {
131
- setActualPlacement("bottom");
132
- } else if (!fitsBelow && fitsAbove) {
133
- setActualPlacement("top");
134
- } else {
135
- setActualPlacement(placement);
142
+ if (onPlacementChange) {
143
+ onPlacementChange({
144
+ placement: resolvedPlacement,
145
+ arrow: middlewareData.arrow
146
+ });
136
147
  }
148
+ }, [resolvedPlacement, middlewareData.arrow, onPlacementChange]);
149
+ const combinedStyles = {
150
+ ...floatingStyles,
151
+ ...!isPositioned ? { opacity: 0, pointerEvents: "none" } : {}
137
152
  };
138
- const handleHorizontalShift = () => {
139
- if (!triggerPosition) {
140
- return;
141
- }
142
- const popoverBoundingRect = ref?.current?.getBoundingClientRect();
143
- if (!popoverBoundingRect) {
144
- return;
145
- }
146
- const { width: popoverWidth } = popoverBoundingRect;
147
- const triggerCenter = triggerPosition.width && triggerPosition.left + triggerPosition.width / 2;
148
- const popoverLeft = align === "center" && triggerCenter ? triggerCenter - popoverWidth / 2 : triggerPosition.left;
149
- const clampedPopoverLeft = clamp(
150
- scrollX,
151
- popoverLeft,
152
- window.innerWidth + scrollX - popoverWidth
153
- );
154
- const triggerRightFromLeft = window.innerWidth - triggerPosition.right;
155
- const clampedTriggerRightFromLeft = clamp(
156
- scrollX + popoverWidth,
157
- triggerRightFromLeft,
158
- scrollX + window.innerWidth
159
- );
160
- if (alignmentAnchor === "right" && clampedTriggerRightFromLeft !== triggerPosition.right + horizontalOffset) {
161
- setHorizontalOffset(
162
- window.innerWidth - clampedTriggerRightFromLeft - triggerPosition.right
163
- );
164
- }
165
- if (alignmentAnchor === "left" && clampedPopoverLeft !== triggerPosition.left + horizontalOffset) {
166
- setHorizontalOffset(clampedPopoverLeft - triggerPosition.left);
167
- }
168
- return;
169
- };
170
- lib_hooks_useIsomorphicLayoutEffect_cjs.useIsomorphicLayoutEffect(() => {
171
- if (!showPopover) {
172
- return;
173
- }
174
- if (width !== "full") {
175
- handleHorizontalShift();
176
- }
177
- if (!lockPlacement) {
178
- handlePlacement();
179
- }
180
- }, [showPopover]);
181
- const triggerPositionVars = triggerPosition && {
182
- // Vertical positioning
183
- [lib_components_private_Popover_Popover_css_cjs.triggerVars[actualPlacement]]: `${triggerPosition[actualPlacement]}`,
184
- // Horizontal positioning
185
- [lib_components_private_Popover_Popover_css_cjs.triggerVars.left]: width === "full" || alignmentAnchor === "left" ? `${triggerPosition?.left}` : void 0,
186
- [lib_components_private_Popover_Popover_css_cjs.triggerVars.right]: width === "full" || alignmentAnchor === "right" ? `${triggerPosition?.right}` : void 0,
187
- // Horizontal scroll offset
188
- [lib_components_private_Popover_Popover_css_cjs.horizontalOffset]: width !== "full" ? `${horizontalOffset}` : "0"
153
+ const popoverContextValue = {
154
+ arrow: middlewareData.arrow,
155
+ resolvedPlacement
189
156
  };
190
- if (!showPopover) {
191
- return null;
192
- }
193
157
  return /* @__PURE__ */ jsxRuntime.jsxs(lib_components_BraidPortal_BraidPortal_cjs.BraidPortal, { children: [
194
158
  modal && /* @__PURE__ */ jsxRuntime.jsx(
195
159
  lib_components_Box_Box_cjs.Box,
@@ -217,17 +181,18 @@ const PopoverContent = react.forwardRef(
217
181
  role: role || void 0,
218
182
  tabIndex: -1,
219
183
  zIndex,
220
- position: "absolute",
221
- marginTop: actualPlacement === "bottom" ? offsetSpace : void 0,
222
- marginBottom: actualPlacement === "top" ? offsetSpace : void 0,
223
- style: triggerPositionVars && dynamic.assignInlineVars(triggerPositionVars),
224
- className: {
225
- [lib_components_private_Popover_Popover_css_cjs.popoverPosition]: true,
226
- [lib_components_private_Popover_Popover_css_cjs.animation]: true,
227
- [lib_components_private_Popover_Popover_css_cjs.invertPlacement]: actualPlacement === "bottom",
228
- [lib_components_private_Popover_Popover_css_cjs.delayVisibility]: delayVisibility
229
- },
230
- children
184
+ style: combinedStyles,
185
+ children: /* @__PURE__ */ jsxRuntime.jsx(
186
+ lib_components_Box_Box_cjs.Box,
187
+ {
188
+ className: {
189
+ [lib_components_private_Popover_Popover_css_cjs.animation]: true,
190
+ [lib_components_private_Popover_Popover_css_cjs.invertPlacement]: resolvedPlacement === "bottom",
191
+ [lib_components_private_Popover_Popover_css_cjs.delayVisibility]: delayVisibility
192
+ },
193
+ children: /* @__PURE__ */ jsxRuntime.jsx(PopoverContext.Provider, { value: popoverContextValue, children })
194
+ }
195
+ )
231
196
  }
232
197
  )
233
198
  ] });
@@ -9,23 +9,6 @@ const backdrop = css.style({
9
9
  width: "100vw",
10
10
  height: "100vh"
11
11
  }, "backdrop");
12
- const triggerVars = {
13
- top: css.createVar("triggerVars_top"),
14
- left: css.createVar("triggerVars_left"),
15
- bottom: css.createVar("triggerVars_bottom"),
16
- right: css.createVar("triggerVars_right")
17
- };
18
- const horizontalOffset = css.createVar("horizontalOffset");
19
- const dynamicHeight = css.createVar("dynamicHeight");
20
- const popoverPosition = css.style({
21
- vars: {
22
- [dynamicHeight]: "100svh"
23
- },
24
- top: cssUtils.calc(triggerVars.bottom).multiply("1px").toString(),
25
- bottom: cssUtils.calc(css.fallbackVar(dynamicHeight, "100vh")).subtract(cssUtils.calc(triggerVars.top).multiply("1px")).toString(),
26
- left: cssUtils.calc(triggerVars.left).add(horizontalOffset).multiply("1px").toString(),
27
- right: cssUtils.calc(triggerVars.right).add(horizontalOffset).multiply("1px").toString()
28
- }, "popoverPosition");
29
12
  const placementModifier = css.createVar("placementModifier");
30
13
  const invertPlacement = css.style({
31
14
  vars: {
@@ -53,8 +36,4 @@ fileScope.endFileScope();
53
36
  exports.animation = animation;
54
37
  exports.backdrop = backdrop;
55
38
  exports.delayVisibility = delayVisibility;
56
- exports.horizontalOffset = horizontalOffset;
57
39
  exports.invertPlacement = invertPlacement;
58
- exports.popoverPosition = popoverPosition;
59
- exports.transitionThreshold = transitionThreshold;
60
- exports.triggerVars = triggerVars;
@@ -1,5 +1,5 @@
1
1
  import { setFileScope, endFileScope } from "@vanilla-extract/css/fileScope";
2
- import { createVar, style, keyframes, fallbackVar } from "@vanilla-extract/css";
2
+ import { style, keyframes, createVar, fallbackVar } from "@vanilla-extract/css";
3
3
  import { calc } from "@vanilla-extract/css-utils";
4
4
  import { animationTimeout } from "../animationTimeout.mjs";
5
5
  import { vars } from "../../../themes/vars.css.mjs";
@@ -8,23 +8,6 @@ const backdrop = style({
8
8
  width: "100vw",
9
9
  height: "100vh"
10
10
  }, "backdrop");
11
- const triggerVars = {
12
- top: createVar("triggerVars_top"),
13
- left: createVar("triggerVars_left"),
14
- bottom: createVar("triggerVars_bottom"),
15
- right: createVar("triggerVars_right")
16
- };
17
- const horizontalOffset = createVar("horizontalOffset");
18
- const dynamicHeight = createVar("dynamicHeight");
19
- const popoverPosition = style({
20
- vars: {
21
- [dynamicHeight]: "100svh"
22
- },
23
- top: calc(triggerVars.bottom).multiply("1px").toString(),
24
- bottom: calc(fallbackVar(dynamicHeight, "100vh")).subtract(calc(triggerVars.top).multiply("1px")).toString(),
25
- left: calc(triggerVars.left).add(horizontalOffset).multiply("1px").toString(),
26
- right: calc(triggerVars.right).add(horizontalOffset).multiply("1px").toString()
27
- }, "popoverPosition");
28
11
  const placementModifier = createVar("placementModifier");
29
12
  const invertPlacement = style({
30
13
  vars: {
@@ -53,9 +36,5 @@ export {
53
36
  animation,
54
37
  backdrop,
55
38
  delayVisibility,
56
- horizontalOffset,
57
- invertPlacement,
58
- popoverPosition,
59
- transitionThreshold,
60
- triggerVars
39
+ invertPlacement
61
40
  };
@@ -1,34 +1,31 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { assignInlineVars } from "@vanilla-extract/dynamic";
2
+ import { offset, flip, size, shift, arrow, useFloating, autoUpdate } from "@floating-ui/react-dom";
3
3
  import dedent from "dedent";
4
- import { forwardRef, useState, useRef, useImperativeHandle, useEffect } from "react";
5
- import { useIsomorphicLayoutEffect } from "../../../hooks/useIsomorphicLayoutEffect.mjs";
4
+ import { forwardRef, useRef, useImperativeHandle, useEffect, createContext } from "react";
6
5
  import { Box } from "../../Box/Box.mjs";
7
6
  import { BraidPortal } from "../../BraidPortal/BraidPortal.mjs";
7
+ import { useResponsiveValue } from "../../useResponsiveValue/useResponsiveValue.mjs";
8
8
  import { useSpace } from "../../useSpace/useSpace.mjs";
9
9
  import { animationTimeout } from "../animationTimeout.mjs";
10
- import { transitionThreshold, triggerVars, backdrop, delayVisibility, invertPlacement, animation, popoverPosition, horizontalOffset } from "./Popover.css.mjs";
11
- const zIndex = "notification";
12
- const getPosition = (element) => {
13
- if (!element) {
14
- return void 0;
10
+ import { backdrop, delayVisibility, invertPlacement, animation } from "./Popover.css.mjs";
11
+ import { normalizeResponsiveValue } from "../../../css/atoms/sprinkles.css.mjs";
12
+ const positionMap = {
13
+ top: {
14
+ left: "top-start",
15
+ center: "top",
16
+ right: "top-end"
17
+ },
18
+ bottom: {
19
+ left: "bottom-start",
20
+ center: "bottom",
21
+ right: "bottom-end"
15
22
  }
16
- const rect = element.getBoundingClientRect();
17
- const { top, bottom, left, right, width } = rect;
18
- const { scrollX: scrollX2, scrollY, innerWidth } = window;
19
- return {
20
- // For `top`, we subtract this from the dynamic viewport height in `Popover.css.ts`
21
- // which can't be accessed from Javascript.
22
- top: top + scrollY,
23
- bottom: bottom + scrollY,
24
- left: left + scrollX2,
25
- right: innerWidth - right - scrollX2,
26
- width
27
- };
28
23
  };
29
- function clamp(min, preferred, max) {
30
- return Math.min(Math.max(preferred, min), max);
24
+ function getFloatingUiPosition(placement, align) {
25
+ return positionMap[placement][align];
31
26
  }
27
+ const zIndex = "notification";
28
+ const PopoverContext = createContext(null);
32
29
  const PopoverContent = forwardRef(
33
30
  ({
34
31
  id,
@@ -42,18 +39,59 @@ const PopoverContent = forwardRef(
42
39
  modal = true,
43
40
  open,
44
41
  onClose,
42
+ onPlacementChange,
45
43
  triggerRef,
46
44
  enterFocusRef,
45
+ arrowRef,
47
46
  children
48
47
  }, forwardedRef) => {
49
- const [triggerPosition, setTriggerPosition] = useState(void 0);
50
48
  const ref = useRef(null);
51
49
  useImperativeHandle(forwardedRef, () => ref.current);
52
- const [horizontalOffset$1, setHorizontalOffset] = useState(0);
53
- const [actualPlacement, setActualPlacement] = useState(placement);
54
- const showPopover = open && triggerPosition;
55
- const transitionThresholdInPx = useSpace().space[transitionThreshold];
56
- const alignmentAnchor = align === "center" ? "left" : align;
50
+ const normalized = normalizeResponsiveValue(offsetSpace);
51
+ const mobile = normalized.mobile ?? "none";
52
+ const tablet = normalized.tablet ?? mobile;
53
+ const desktop = normalized.desktop ?? tablet;
54
+ const wide = normalized.wide ?? desktop;
55
+ const resolvedOffsetSpace = useResponsiveValue()({ mobile, tablet, desktop, wide }) ?? mobile;
56
+ const spaceScale = useSpace();
57
+ let offsetSpacePx = 0;
58
+ if (resolvedOffsetSpace !== "none") {
59
+ offsetSpacePx = spaceScale.space[resolvedOffsetSpace] * spaceScale.grid;
60
+ }
61
+ const floatingUiRequestedPosition = getFloatingUiPosition(placement, align);
62
+ const middleware = [
63
+ offset(offsetSpacePx),
64
+ !lockPlacement && flip(),
65
+ width === "full" ? size({
66
+ apply({ rects, elements }) {
67
+ Object.assign(elements.floating.style, {
68
+ width: `${rects.reference.width}px`
69
+ });
70
+ }
71
+ }) : shift({
72
+ crossAxis: align === "center"
73
+ }),
74
+ arrowRef && arrow({ element: arrowRef })
75
+ ].filter(Boolean);
76
+ const {
77
+ refs,
78
+ floatingStyles,
79
+ middlewareData,
80
+ placement: floatingUiEvaluatedPosition,
81
+ isPositioned
82
+ } = useFloating({
83
+ placement: floatingUiRequestedPosition,
84
+ middleware,
85
+ whileElementsMounted: autoUpdate
86
+ });
87
+ useEffect(() => {
88
+ refs.setReference(triggerRef.current);
89
+ }, [triggerRef, refs]);
90
+ useEffect(() => {
91
+ if (ref.current) {
92
+ refs.setFloating(ref.current);
93
+ }
94
+ }, [refs]);
57
95
  useEffect(() => {
58
96
  const handleKeydown = (event) => {
59
97
  if (event.key === "Escape" || event.key === "Tab") {
@@ -78,9 +116,6 @@ const PopoverContent = forwardRef(
78
116
  window.removeEventListener("keydown", handleKeydown);
79
117
  };
80
118
  }, [onClose, triggerRef]);
81
- useEffect(() => {
82
- setTriggerPosition(getPosition(triggerRef.current));
83
- }, [triggerRef]);
84
119
  useEffect(() => {
85
120
  setTimeout(() => {
86
121
  if (!enterFocusRef) {
@@ -98,95 +133,24 @@ const PopoverContent = forwardRef(
98
133
  }
99
134
  }
100
135
  }, animationTimeout);
101
- }, [open, enterFocusRef, triggerRef]);
136
+ }, [open, enterFocusRef]);
137
+ const resolvedPlacement = floatingUiEvaluatedPosition?.startsWith("top") ? "top" : "bottom";
102
138
  useEffect(() => {
103
- const handleResize = () => {
104
- setTriggerPosition(getPosition(triggerRef.current));
105
- };
106
- window.addEventListener("resize", handleResize);
107
- const resizeObserver = new ResizeObserver(handleResize);
108
- resizeObserver.observe(document.body);
109
- return () => {
110
- window.removeEventListener("resize", handleResize);
111
- resizeObserver.disconnect();
112
- };
113
- }, [triggerRef]);
114
- const handlePlacement = () => {
115
- const popoverBoundingRect = ref?.current?.getBoundingClientRect();
116
- if (!popoverBoundingRect) {
117
- return;
118
- }
119
- const triggerBoundingRect = triggerRef.current?.getBoundingClientRect();
120
- if (!triggerBoundingRect) {
121
- return;
122
- }
123
- const { height: popoverHeight } = popoverBoundingRect;
124
- const heightRequired = popoverHeight + transitionThresholdInPx;
125
- const fitsAbove = triggerBoundingRect.top >= heightRequired;
126
- const fitsBelow = window.innerHeight - triggerBoundingRect.bottom >= heightRequired;
127
- if (!fitsAbove && fitsBelow) {
128
- setActualPlacement("bottom");
129
- } else if (!fitsBelow && fitsAbove) {
130
- setActualPlacement("top");
131
- } else {
132
- setActualPlacement(placement);
139
+ if (onPlacementChange) {
140
+ onPlacementChange({
141
+ placement: resolvedPlacement,
142
+ arrow: middlewareData.arrow
143
+ });
133
144
  }
145
+ }, [resolvedPlacement, middlewareData.arrow, onPlacementChange]);
146
+ const combinedStyles = {
147
+ ...floatingStyles,
148
+ ...!isPositioned ? { opacity: 0, pointerEvents: "none" } : {}
134
149
  };
135
- const handleHorizontalShift = () => {
136
- if (!triggerPosition) {
137
- return;
138
- }
139
- const popoverBoundingRect = ref?.current?.getBoundingClientRect();
140
- if (!popoverBoundingRect) {
141
- return;
142
- }
143
- const { width: popoverWidth } = popoverBoundingRect;
144
- const triggerCenter = triggerPosition.width && triggerPosition.left + triggerPosition.width / 2;
145
- const popoverLeft = align === "center" && triggerCenter ? triggerCenter - popoverWidth / 2 : triggerPosition.left;
146
- const clampedPopoverLeft = clamp(
147
- scrollX,
148
- popoverLeft,
149
- window.innerWidth + scrollX - popoverWidth
150
- );
151
- const triggerRightFromLeft = window.innerWidth - triggerPosition.right;
152
- const clampedTriggerRightFromLeft = clamp(
153
- scrollX + popoverWidth,
154
- triggerRightFromLeft,
155
- scrollX + window.innerWidth
156
- );
157
- if (alignmentAnchor === "right" && clampedTriggerRightFromLeft !== triggerPosition.right + horizontalOffset$1) {
158
- setHorizontalOffset(
159
- window.innerWidth - clampedTriggerRightFromLeft - triggerPosition.right
160
- );
161
- }
162
- if (alignmentAnchor === "left" && clampedPopoverLeft !== triggerPosition.left + horizontalOffset$1) {
163
- setHorizontalOffset(clampedPopoverLeft - triggerPosition.left);
164
- }
165
- return;
166
- };
167
- useIsomorphicLayoutEffect(() => {
168
- if (!showPopover) {
169
- return;
170
- }
171
- if (width !== "full") {
172
- handleHorizontalShift();
173
- }
174
- if (!lockPlacement) {
175
- handlePlacement();
176
- }
177
- }, [showPopover]);
178
- const triggerPositionVars = triggerPosition && {
179
- // Vertical positioning
180
- [triggerVars[actualPlacement]]: `${triggerPosition[actualPlacement]}`,
181
- // Horizontal positioning
182
- [triggerVars.left]: width === "full" || alignmentAnchor === "left" ? `${triggerPosition?.left}` : void 0,
183
- [triggerVars.right]: width === "full" || alignmentAnchor === "right" ? `${triggerPosition?.right}` : void 0,
184
- // Horizontal scroll offset
185
- [horizontalOffset]: width !== "full" ? `${horizontalOffset$1}` : "0"
150
+ const popoverContextValue = {
151
+ arrow: middlewareData.arrow,
152
+ resolvedPlacement
186
153
  };
187
- if (!showPopover) {
188
- return null;
189
- }
190
154
  return /* @__PURE__ */ jsxs(BraidPortal, { children: [
191
155
  modal && /* @__PURE__ */ jsx(
192
156
  Box,
@@ -214,17 +178,18 @@ const PopoverContent = forwardRef(
214
178
  role: role || void 0,
215
179
  tabIndex: -1,
216
180
  zIndex,
217
- position: "absolute",
218
- marginTop: actualPlacement === "bottom" ? offsetSpace : void 0,
219
- marginBottom: actualPlacement === "top" ? offsetSpace : void 0,
220
- style: triggerPositionVars && assignInlineVars(triggerPositionVars),
221
- className: {
222
- [popoverPosition]: true,
223
- [animation]: true,
224
- [invertPlacement]: actualPlacement === "bottom",
225
- [delayVisibility]: delayVisibility$1
226
- },
227
- children
181
+ style: combinedStyles,
182
+ children: /* @__PURE__ */ jsx(
183
+ Box,
184
+ {
185
+ className: {
186
+ [animation]: true,
187
+ [invertPlacement]: resolvedPlacement === "bottom",
188
+ [delayVisibility]: delayVisibility$1
189
+ },
190
+ children: /* @__PURE__ */ jsx(PopoverContext.Provider, { value: popoverContextValue, children })
191
+ }
192
+ )
228
193
  }
229
194
  )
230
195
  ] });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "braid-design-system",
3
- "version": "33.12.3",
3
+ "version": "33.12.4",
4
4
  "description": "Themeable design system for the SEEK Group",
5
5
  "homepage": "https://seek-oss.github.io/braid-design-system/",
6
6
  "bugs": {
@@ -164,6 +164,7 @@
164
164
  "@capsizecss/core": "^4.1.2",
165
165
  "@capsizecss/metrics": "^3.0.0",
166
166
  "@capsizecss/vanilla-extract": "^2.0.4",
167
+ "@floating-ui/react-dom": "^2.1.6",
167
168
  "@vanilla-extract/css": "^1.9.2",
168
169
  "@vanilla-extract/css-utils": "^0.1.3",
169
170
  "@vanilla-extract/dynamic": "^2.1.2",