@simplybusiness/mobius 10.4.2 → 10.4.3
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 +7 -0
- package/dist/cjs/components/Popover/Arrow.js +43 -0
- package/dist/cjs/components/Popover/Arrow.js.map +7 -0
- package/dist/cjs/components/Popover/Popover.js +258 -83
- package/dist/cjs/components/Popover/Popover.js.map +4 -4
- package/dist/cjs/components/Popover/index.js +258 -83
- package/dist/cjs/components/Popover/index.js.map +4 -4
- package/dist/cjs/components/Popover/useAutoUpdate.js +53 -0
- package/dist/cjs/components/Popover/useAutoUpdate.js.map +7 -0
- package/dist/cjs/components/Popover/useFloatingPosition.js +128 -0
- package/dist/cjs/components/Popover/useFloatingPosition.js.map +7 -0
- package/dist/cjs/components/Popover/useOutsidePress.js +46 -0
- package/dist/cjs/components/Popover/useOutsidePress.js.map +7 -0
- package/dist/cjs/components/index.js +422 -245
- package/dist/cjs/components/index.js.map +4 -4
- package/dist/cjs/index.js +422 -245
- package/dist/cjs/index.js.map +4 -4
- package/dist/cjs/meta.json +316 -32
- package/dist/esm/chunk-26KZYRE6.js +108 -0
- package/dist/esm/chunk-26KZYRE6.js.map +7 -0
- package/dist/esm/chunk-CAL44W47.js +23 -0
- package/dist/esm/chunk-CAL44W47.js.map +7 -0
- package/dist/esm/{chunk-PEEQNAIN.js → chunk-DMYDWKKA.js} +4 -4
- package/dist/esm/chunk-K3ECDAUR.js +33 -0
- package/dist/esm/chunk-K3ECDAUR.js.map +7 -0
- package/dist/esm/{chunk-GJBH37DH.js → chunk-KFHPI67N.js} +4 -4
- package/dist/esm/{chunk-F5ELD54X.js → chunk-LGZWQZLS.js} +2 -2
- package/dist/esm/{chunk-OAG5T7NC.js → chunk-NEFRXIFY.js} +4 -4
- package/dist/esm/chunk-VZ3IWSK6.js +158 -0
- package/dist/esm/chunk-VZ3IWSK6.js.map +7 -0
- package/dist/esm/chunk-WYJP7HVL.js +26 -0
- package/dist/esm/chunk-WYJP7HVL.js.map +7 -0
- package/dist/esm/components/AddressLookup/AddressLookup.js +4 -4
- package/dist/esm/components/AddressLookup/index.js +6 -6
- package/dist/esm/components/Breadcrumbs/index.js +3 -3
- package/dist/esm/components/Checkbox/index.js +1 -1
- package/dist/esm/components/Combobox/Combobox.js +3 -3
- package/dist/esm/components/Combobox/index.js +3 -3
- package/dist/esm/components/Drawer/index.js +3 -3
- package/dist/esm/components/Modal/index.js +3 -3
- package/dist/esm/components/Popover/Arrow.js +8 -0
- package/dist/esm/components/Popover/Arrow.js.map +7 -0
- package/dist/esm/components/Popover/Popover.js +5 -1
- package/dist/esm/components/Popover/index.js +5 -1
- package/dist/esm/components/Popover/useAutoUpdate.js +8 -0
- package/dist/esm/components/Popover/useAutoUpdate.js.map +7 -0
- package/dist/esm/components/Popover/useFloatingPosition.js +8 -0
- package/dist/esm/components/Popover/useFloatingPosition.js.map +7 -0
- package/dist/esm/components/Popover/useOutsidePress.js +8 -0
- package/dist/esm/components/Popover/useOutsidePress.js.map +7 -0
- package/dist/esm/components/index.js +77 -73
- package/dist/esm/index.js +77 -73
- package/dist/esm/meta.json +3737 -3401
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/components/Popover/Arrow.d.ts +9 -0
- package/dist/types/components/Popover/useAutoUpdate.d.ts +9 -0
- package/dist/types/components/Popover/useFloatingPosition.d.ts +17 -0
- package/dist/types/components/Popover/useOutsidePress.d.ts +9 -0
- package/package.json +1 -2
- package/src/components/Popover/Arrow.tsx +25 -0
- package/src/components/Popover/Popover.characterization.test.tsx +269 -0
- package/src/components/Popover/Popover.stories.tsx +40 -3
- package/src/components/Popover/Popover.test.tsx +6 -2
- package/src/components/Popover/Popover.tsx +87 -81
- package/src/components/Popover/useAutoUpdate.ts +43 -0
- package/src/components/Popover/useFloatingPosition.ts +177 -0
- package/src/components/Popover/useOutsidePress.ts +31 -0
- package/dist/esm/chunk-O5YEU5TG.js +0 -155
- package/dist/esm/chunk-O5YEU5TG.js.map +0 -7
- /package/dist/esm/{chunk-PEEQNAIN.js.map → chunk-DMYDWKKA.js.map} +0 -0
- /package/dist/esm/{chunk-GJBH37DH.js.map → chunk-KFHPI67N.js.map} +0 -0
- /package/dist/esm/{chunk-F5ELD54X.js.map → chunk-LGZWQZLS.js.map} +0 -0
- /package/dist/esm/{chunk-OAG5T7NC.js.map → chunk-NEFRXIFY.js.map} +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { RefObject } from "react";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
|
|
4
|
+
interface UseAutoUpdateArgs {
|
|
5
|
+
referenceRef: RefObject<HTMLElement | null>;
|
|
6
|
+
floatingRef: RefObject<HTMLDivElement | null>;
|
|
7
|
+
onUpdate: () => void;
|
|
8
|
+
enabled: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useAutoUpdate = ({
|
|
12
|
+
referenceRef,
|
|
13
|
+
floatingRef,
|
|
14
|
+
onUpdate,
|
|
15
|
+
enabled,
|
|
16
|
+
}: UseAutoUpdateArgs): void => {
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!enabled) return;
|
|
19
|
+
|
|
20
|
+
const reference = referenceRef.current;
|
|
21
|
+
const floating = floatingRef.current;
|
|
22
|
+
|
|
23
|
+
const scrollOpts: AddEventListenerOptions = {
|
|
24
|
+
capture: true,
|
|
25
|
+
passive: true,
|
|
26
|
+
};
|
|
27
|
+
window.addEventListener("scroll", onUpdate, scrollOpts);
|
|
28
|
+
window.addEventListener("resize", onUpdate, { passive: true });
|
|
29
|
+
|
|
30
|
+
const observer =
|
|
31
|
+
typeof ResizeObserver === "function"
|
|
32
|
+
? new ResizeObserver(onUpdate)
|
|
33
|
+
: null;
|
|
34
|
+
if (observer && reference) observer.observe(reference);
|
|
35
|
+
if (observer && floating) observer.observe(floating);
|
|
36
|
+
|
|
37
|
+
return () => {
|
|
38
|
+
window.removeEventListener("scroll", onUpdate, scrollOpts);
|
|
39
|
+
window.removeEventListener("resize", onUpdate);
|
|
40
|
+
observer?.disconnect();
|
|
41
|
+
};
|
|
42
|
+
}, [enabled, onUpdate, referenceRef, floatingRef]);
|
|
43
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import type { CSSProperties, RefObject } from "react";
|
|
2
|
+
import { useCallback, useLayoutEffect } from "react";
|
|
3
|
+
|
|
4
|
+
type Placement = "bottom" | "top";
|
|
5
|
+
|
|
6
|
+
interface UseFloatingPositionArgs {
|
|
7
|
+
referenceRef: RefObject<HTMLElement | null>;
|
|
8
|
+
floatingRef: RefObject<HTMLDivElement | null>;
|
|
9
|
+
arrowRef: RefObject<SVGSVGElement | null>;
|
|
10
|
+
isOpen: boolean;
|
|
11
|
+
offsetPx: number;
|
|
12
|
+
arrowWidth: number;
|
|
13
|
+
useFixedStrategy: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface UseFloatingPositionResult {
|
|
17
|
+
initialFloatingStyles: CSSProperties;
|
|
18
|
+
initialArrowStyles: CSSProperties;
|
|
19
|
+
update: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Two positioning strategies:
|
|
23
|
+
// - `absolute` + document-relative coords (viewport rect + window.scrollY/X)
|
|
24
|
+
// when portalled to document.body. The initial containing block is the
|
|
25
|
+
// document, so absolute coords pin to the trigger and scroll natively.
|
|
26
|
+
// - `fixed` + viewport coords when portalled inside a <dialog> (top-layer).
|
|
27
|
+
// The dialog's own box would otherwise become the containing block for an
|
|
28
|
+
// `absolute` child, throwing off coordinates and triggering `overflow: hidden`
|
|
29
|
+
// clipping. `fixed` keeps the containing block as the viewport.
|
|
30
|
+
const ABSOLUTE_FLOATING_STYLES: CSSProperties = {
|
|
31
|
+
position: "absolute",
|
|
32
|
+
top: 0,
|
|
33
|
+
left: 0,
|
|
34
|
+
width: "max-content",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const FIXED_FLOATING_STYLES: CSSProperties = {
|
|
38
|
+
position: "fixed",
|
|
39
|
+
top: 0,
|
|
40
|
+
left: 0,
|
|
41
|
+
width: "max-content",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const INITIAL_ARROW_STYLES: CSSProperties = {
|
|
45
|
+
position: "absolute",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const VIEWPORT_PADDING = 0;
|
|
49
|
+
|
|
50
|
+
// Properties that make an ancestor the containing block for a `position: fixed`
|
|
51
|
+
// descendant, overriding the initial viewport containing block. When the portal
|
|
52
|
+
// target (dialog) has any of these, `position: fixed` coords are interpreted
|
|
53
|
+
// relative to the dialog's box, not the viewport — we compensate by subtracting
|
|
54
|
+
// the dialog's viewport offset.
|
|
55
|
+
const createsFixedContainingBlock = (el: HTMLElement): boolean => {
|
|
56
|
+
const style = window.getComputedStyle(el);
|
|
57
|
+
return (
|
|
58
|
+
style.transform !== "none" ||
|
|
59
|
+
style.filter !== "none" ||
|
|
60
|
+
style.backdropFilter !== "none" ||
|
|
61
|
+
style.perspective !== "none" ||
|
|
62
|
+
/\b(transform|filter|perspective)\b/.test(style.willChange)
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const useFloatingPosition = ({
|
|
67
|
+
referenceRef,
|
|
68
|
+
floatingRef,
|
|
69
|
+
arrowRef,
|
|
70
|
+
isOpen,
|
|
71
|
+
offsetPx,
|
|
72
|
+
arrowWidth,
|
|
73
|
+
useFixedStrategy,
|
|
74
|
+
}: UseFloatingPositionArgs): UseFloatingPositionResult => {
|
|
75
|
+
// Writes styles directly to the DOM (bypassing React state) so scroll/resize
|
|
76
|
+
// handlers can update position in the same event-frame the browser uses to
|
|
77
|
+
// paint the scroll, eliminating the re-render lag that shows up as a
|
|
78
|
+
// trigger-vs-popover parallax on fast scroll.
|
|
79
|
+
const update = useCallback(() => {
|
|
80
|
+
const reference = referenceRef.current;
|
|
81
|
+
const floating = floatingRef.current;
|
|
82
|
+
if (!reference || !floating) return;
|
|
83
|
+
|
|
84
|
+
const refRect = reference.getBoundingClientRect();
|
|
85
|
+
const floatingWidth = floating.offsetWidth;
|
|
86
|
+
const floatingHeight = floating.offsetHeight;
|
|
87
|
+
const scrollX = useFixedStrategy ? 0 : window.scrollX;
|
|
88
|
+
const scrollY = useFixedStrategy ? 0 : window.scrollY;
|
|
89
|
+
|
|
90
|
+
// Clamp to the portal target's bounds when the dialog is a containing
|
|
91
|
+
// block (its own `overflow: hidden` would clip the popover). Otherwise
|
|
92
|
+
// clamp to the viewport.
|
|
93
|
+
const parent = floating.parentElement;
|
|
94
|
+
const parentIsCb =
|
|
95
|
+
useFixedStrategy && !!parent && createsFixedContainingBlock(parent);
|
|
96
|
+
const boundsRect = parentIsCb
|
|
97
|
+
? parent.getBoundingClientRect()
|
|
98
|
+
: {
|
|
99
|
+
left: 0,
|
|
100
|
+
top: 0,
|
|
101
|
+
right: window.innerWidth,
|
|
102
|
+
bottom: window.innerHeight,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const bottomTopViewport = refRect.bottom + offsetPx;
|
|
106
|
+
const topTopViewport = refRect.top - floatingHeight - offsetPx;
|
|
107
|
+
const overflowsBottom =
|
|
108
|
+
bottomTopViewport + floatingHeight > boundsRect.bottom;
|
|
109
|
+
const fitsTop = topTopViewport >= boundsRect.top;
|
|
110
|
+
const nextPlacement: Placement =
|
|
111
|
+
overflowsBottom && fitsTop ? "top" : "bottom";
|
|
112
|
+
|
|
113
|
+
const topViewport =
|
|
114
|
+
nextPlacement === "bottom" ? bottomTopViewport : topTopViewport;
|
|
115
|
+
|
|
116
|
+
const rawLeftViewport =
|
|
117
|
+
refRect.left + refRect.width / 2 - floatingWidth / 2;
|
|
118
|
+
const minLeftViewport = boundsRect.left + VIEWPORT_PADDING;
|
|
119
|
+
const maxLeftViewport = Math.max(
|
|
120
|
+
minLeftViewport,
|
|
121
|
+
boundsRect.right - floatingWidth - VIEWPORT_PADDING,
|
|
122
|
+
);
|
|
123
|
+
const leftViewport = Math.min(
|
|
124
|
+
Math.max(minLeftViewport, rawLeftViewport),
|
|
125
|
+
maxLeftViewport,
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Subtract containing-block offset when the dialog is the CB, so
|
|
129
|
+
// `position: fixed` coords land at the intended viewport position.
|
|
130
|
+
const cbOffsetLeft = parentIsCb ? boundsRect.left : 0;
|
|
131
|
+
const cbOffsetTop = parentIsCb ? boundsRect.top : 0;
|
|
132
|
+
|
|
133
|
+
floating.style.top = `${topViewport + scrollY - cbOffsetTop}px`;
|
|
134
|
+
floating.style.left = `${leftViewport + scrollX - cbOffsetLeft}px`;
|
|
135
|
+
|
|
136
|
+
const arrow = arrowRef.current;
|
|
137
|
+
if (!arrow) return;
|
|
138
|
+
|
|
139
|
+
// Arrow coords are relative to the floating element, so scroll cancels out.
|
|
140
|
+
const arrowHalf = arrowWidth / 2;
|
|
141
|
+
const refCenterX = refRect.left + refRect.width / 2;
|
|
142
|
+
const rawArrowLeft = refCenterX - leftViewport - arrowHalf;
|
|
143
|
+
const maxArrowLeft = Math.max(0, floatingWidth - arrowWidth);
|
|
144
|
+
const arrowLeft = Math.min(Math.max(0, rawArrowLeft), maxArrowLeft);
|
|
145
|
+
|
|
146
|
+
arrow.style.left = `${arrowLeft}px`;
|
|
147
|
+
if (nextPlacement === "bottom") {
|
|
148
|
+
arrow.style.bottom = "100%";
|
|
149
|
+
arrow.style.top = "";
|
|
150
|
+
arrow.style.transform = "rotate(180deg)";
|
|
151
|
+
} else {
|
|
152
|
+
arrow.style.top = "100%";
|
|
153
|
+
arrow.style.bottom = "";
|
|
154
|
+
arrow.style.transform = "";
|
|
155
|
+
}
|
|
156
|
+
}, [
|
|
157
|
+
referenceRef,
|
|
158
|
+
floatingRef,
|
|
159
|
+
arrowRef,
|
|
160
|
+
offsetPx,
|
|
161
|
+
arrowWidth,
|
|
162
|
+
useFixedStrategy,
|
|
163
|
+
]);
|
|
164
|
+
|
|
165
|
+
useLayoutEffect(() => {
|
|
166
|
+
if (!isOpen) return;
|
|
167
|
+
update();
|
|
168
|
+
}, [isOpen, update]);
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
initialFloatingStyles: useFixedStrategy
|
|
172
|
+
? FIXED_FLOATING_STYLES
|
|
173
|
+
: ABSOLUTE_FLOATING_STYLES,
|
|
174
|
+
initialArrowStyles: INITIAL_ARROW_STYLES,
|
|
175
|
+
update,
|
|
176
|
+
};
|
|
177
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { RefObject } from "react";
|
|
2
|
+
import { useEffect } from "react";
|
|
3
|
+
|
|
4
|
+
interface UseOutsidePressArgs {
|
|
5
|
+
referenceRef: RefObject<HTMLElement | null>;
|
|
6
|
+
floatingRef: RefObject<HTMLDivElement | null>;
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
onOutsidePress: (event: PointerEvent) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const useOutsidePress = ({
|
|
12
|
+
referenceRef,
|
|
13
|
+
floatingRef,
|
|
14
|
+
enabled,
|
|
15
|
+
onOutsidePress,
|
|
16
|
+
}: UseOutsidePressArgs): void => {
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!enabled) return;
|
|
19
|
+
|
|
20
|
+
const handler = (event: PointerEvent) => {
|
|
21
|
+
const target = event.target as Node | null;
|
|
22
|
+
if (!target) return;
|
|
23
|
+
if (referenceRef.current?.contains(target)) return;
|
|
24
|
+
if (floatingRef.current?.contains(target)) return;
|
|
25
|
+
onOutsidePress(event);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
document.addEventListener("pointerdown", handler);
|
|
29
|
+
return () => document.removeEventListener("pointerdown", handler);
|
|
30
|
+
}, [enabled, onOutsidePress, referenceRef, floatingRef]);
|
|
31
|
+
};
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Button
|
|
3
|
-
} from "./chunk-FD3JTY5L.js";
|
|
4
|
-
import {
|
|
5
|
-
Icon
|
|
6
|
-
} from "./chunk-TKIP5Q5H.js";
|
|
7
|
-
|
|
8
|
-
// src/components/Popover/Popover.tsx
|
|
9
|
-
import {
|
|
10
|
-
FloatingArrow,
|
|
11
|
-
arrow,
|
|
12
|
-
autoUpdate,
|
|
13
|
-
flip,
|
|
14
|
-
offset,
|
|
15
|
-
shift,
|
|
16
|
-
useDismiss,
|
|
17
|
-
useFloating,
|
|
18
|
-
useInteractions
|
|
19
|
-
} from "@floating-ui/react";
|
|
20
|
-
import { cross } from "@simplybusiness/icons";
|
|
21
|
-
import classNames from "classnames/dedupe";
|
|
22
|
-
import { cloneElement, useCallback, useEffect, useRef, useState } from "react";
|
|
23
|
-
import { useWindowEvent } from "@simplybusiness/mobius-hooks";
|
|
24
|
-
import "@simplybusiness/mobius/src/components/Popover/Popover.css";
|
|
25
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
26
|
-
var OFFSET_FROM_CONTENT_DEFAULT = 10;
|
|
27
|
-
var Popover = (props) => {
|
|
28
|
-
const { trigger, children, onOpen, onClose, className } = props;
|
|
29
|
-
const arrowRef = useRef(null);
|
|
30
|
-
const floatingContainerRef = useRef(null);
|
|
31
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
32
|
-
const { refs, floatingStyles, context } = useFloating({
|
|
33
|
-
open: isOpen,
|
|
34
|
-
onOpenChange: setIsOpen,
|
|
35
|
-
whileElementsMounted: autoUpdate,
|
|
36
|
-
middleware: [
|
|
37
|
-
arrow({
|
|
38
|
-
element: arrowRef
|
|
39
|
-
}),
|
|
40
|
-
offset(OFFSET_FROM_CONTENT_DEFAULT),
|
|
41
|
-
shift(),
|
|
42
|
-
flip()
|
|
43
|
-
]
|
|
44
|
-
});
|
|
45
|
-
const dismiss = useDismiss(context, {
|
|
46
|
-
bubbles: true,
|
|
47
|
-
outsidePress: (event) => {
|
|
48
|
-
const toggle = refs.reference.current;
|
|
49
|
-
const isToggleClick = !toggle?.contains(event.target);
|
|
50
|
-
if (isToggleClick) {
|
|
51
|
-
onClose?.();
|
|
52
|
-
}
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
const { getReferenceProps, getFloatingProps } = useInteractions([dismiss]);
|
|
57
|
-
const containerClasses = classNames(
|
|
58
|
-
"mobius",
|
|
59
|
-
"mobius-popover__container",
|
|
60
|
-
className
|
|
61
|
-
);
|
|
62
|
-
const setFloatingRef = useCallback(
|
|
63
|
-
(node) => {
|
|
64
|
-
refs.setFloating(node);
|
|
65
|
-
floatingContainerRef.current = node;
|
|
66
|
-
},
|
|
67
|
-
[refs]
|
|
68
|
-
);
|
|
69
|
-
useEffect(() => {
|
|
70
|
-
const el = floatingContainerRef.current;
|
|
71
|
-
if (!el) return;
|
|
72
|
-
const preventLabelActivation = (e) => {
|
|
73
|
-
const target = e.target;
|
|
74
|
-
if (!target.closest("a[href], input, select, textarea")) {
|
|
75
|
-
e.preventDefault();
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
el.addEventListener("click", preventLabelActivation);
|
|
79
|
-
return () => el.removeEventListener("click", preventLabelActivation);
|
|
80
|
-
}, [isOpen]);
|
|
81
|
-
const toggleVisibility = () => {
|
|
82
|
-
if (isOpen) {
|
|
83
|
-
setIsOpen(false);
|
|
84
|
-
onClose?.();
|
|
85
|
-
return;
|
|
86
|
-
}
|
|
87
|
-
setIsOpen(true);
|
|
88
|
-
onOpen?.();
|
|
89
|
-
};
|
|
90
|
-
const triggerComponent = cloneElement(trigger, {
|
|
91
|
-
ref: refs.setReference,
|
|
92
|
-
className: classNames(
|
|
93
|
-
trigger.props.className,
|
|
94
|
-
"mobius-popover__toggle"
|
|
95
|
-
),
|
|
96
|
-
onClick: toggleVisibility,
|
|
97
|
-
...getReferenceProps()
|
|
98
|
-
});
|
|
99
|
-
useWindowEvent("keydown", (e) => {
|
|
100
|
-
if (e.key === "Escape") {
|
|
101
|
-
onClose?.();
|
|
102
|
-
e.preventDefault();
|
|
103
|
-
e.stopPropagation();
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
107
|
-
triggerComponent,
|
|
108
|
-
isOpen && /* @__PURE__ */ jsxs(
|
|
109
|
-
"div",
|
|
110
|
-
{
|
|
111
|
-
className: containerClasses,
|
|
112
|
-
ref: setFloatingRef,
|
|
113
|
-
style: floatingStyles,
|
|
114
|
-
...getFloatingProps(),
|
|
115
|
-
children: [
|
|
116
|
-
/* @__PURE__ */ jsxs("div", { className: "mobius-popover", children: [
|
|
117
|
-
/* @__PURE__ */ jsx("header", { className: "mobius-popover__header", children: /* @__PURE__ */ jsx(
|
|
118
|
-
Button,
|
|
119
|
-
{
|
|
120
|
-
type: "button",
|
|
121
|
-
className: "mobius-popover__close-button",
|
|
122
|
-
onClick: toggleVisibility,
|
|
123
|
-
"aria-label": "Close",
|
|
124
|
-
variant: "ghost",
|
|
125
|
-
children: /* @__PURE__ */ jsx(
|
|
126
|
-
Icon,
|
|
127
|
-
{
|
|
128
|
-
icon: cross,
|
|
129
|
-
size: "md",
|
|
130
|
-
className: "mobius-popover__close-icon"
|
|
131
|
-
}
|
|
132
|
-
)
|
|
133
|
-
}
|
|
134
|
-
) }),
|
|
135
|
-
/* @__PURE__ */ jsx("div", { className: "mobius-popover__body", children })
|
|
136
|
-
] }),
|
|
137
|
-
/* @__PURE__ */ jsx(
|
|
138
|
-
FloatingArrow,
|
|
139
|
-
{
|
|
140
|
-
ref: arrowRef,
|
|
141
|
-
context,
|
|
142
|
-
width: 20,
|
|
143
|
-
className: "mobius-popover__arrow-icon"
|
|
144
|
-
}
|
|
145
|
-
)
|
|
146
|
-
]
|
|
147
|
-
}
|
|
148
|
-
)
|
|
149
|
-
] });
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
export {
|
|
153
|
-
Popover
|
|
154
|
-
};
|
|
155
|
-
//# sourceMappingURL=chunk-O5YEU5TG.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/components/Popover/Popover.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n FloatingArrow,\n arrow,\n autoUpdate,\n flip,\n offset,\n shift,\n useDismiss,\n useFloating,\n useInteractions,\n} from \"@floating-ui/react\";\nimport { cross } from \"@simplybusiness/icons\";\nimport classNames from \"classnames/dedupe\";\nimport type { ReactElement, ReactNode, RefAttributes } from \"react\";\nimport { cloneElement, useCallback, useEffect, useRef, useState } from \"react\";\nimport { useWindowEvent } from \"@simplybusiness/mobius-hooks\";\nimport type { DOMProps } from \"../../types\";\nimport { Button } from \"../Button\";\nimport { Icon } from \"../Icon\";\nimport \"./Popover.css\";\n\nexport type PopoverElementType = HTMLDivElement;\n\nexport interface PopoverProps\n extends DOMProps, RefAttributes<PopoverElementType> {\n children?: ReactNode;\n trigger: ReactElement;\n /** Callback that fires each time the accordion is opened */\n onOpen?: () => void;\n /** Callback that fires each time the accordion is closed */\n onClose?: () => void;\n /** Custom class name for setting specific CSS */\n className?: string;\n}\n\nconst OFFSET_FROM_CONTENT_DEFAULT = 10;\n\nexport const Popover = (props: PopoverProps) => {\n const { trigger, children, onOpen, onClose, className } = props;\n const arrowRef = useRef(null);\n const floatingContainerRef = useRef<HTMLDivElement | null>(null);\n const [isOpen, setIsOpen] = useState(false);\n const { refs, floatingStyles, context } = useFloating({\n open: isOpen,\n onOpenChange: setIsOpen,\n whileElementsMounted: autoUpdate,\n middleware: [\n arrow({\n element: arrowRef,\n }),\n offset(OFFSET_FROM_CONTENT_DEFAULT),\n shift(),\n flip(),\n ],\n });\n const dismiss = useDismiss(context, {\n bubbles: true,\n outsidePress: (event: MouseEvent) => {\n // Prevent 'onClose' from firing when clicking the toggle to close\n const toggle = refs.reference.current as HTMLElement;\n const isToggleClick = !toggle?.contains(event.target as HTMLElement);\n if (isToggleClick) {\n onClose?.();\n }\n return true;\n },\n });\n const { getReferenceProps, getFloatingProps } = useInteractions([dismiss]);\n\n const containerClasses = classNames(\n \"mobius\",\n \"mobius-popover__container\",\n className,\n );\n\n const setFloatingRef = useCallback(\n (node: HTMLDivElement | null) => {\n refs.setFloating(node);\n floatingContainerRef.current = node;\n },\n [refs],\n );\n\n // Native listener to prevent clicks inside the popover from activating\n // interactive ancestors. Must be native because React's onClick fires\n // too late via delegation.\n useEffect(() => {\n const el = floatingContainerRef.current;\n if (!el) return;\n\n const preventLabelActivation = (e: Event) => {\n const target = e.target as HTMLElement;\n // Allow default behavior for interactive elements (links, inputs, etc.)\n // so they remain functional inside the popover.\n if (!target.closest(\"a[href], input, select, textarea\")) {\n e.preventDefault();\n }\n };\n el.addEventListener(\"click\", preventLabelActivation);\n return () => el.removeEventListener(\"click\", preventLabelActivation);\n }, [isOpen]);\n\n const toggleVisibility = () => {\n if (isOpen) {\n setIsOpen(false);\n onClose?.();\n return;\n }\n\n setIsOpen(true);\n onOpen?.();\n };\n\n const triggerComponent = cloneElement(trigger, {\n ref: refs.setReference,\n className: classNames(\n (trigger.props as { className?: string }).className,\n \"mobius-popover__toggle\",\n ),\n onClick: toggleVisibility,\n ...getReferenceProps(),\n } as Record<string, unknown>);\n\n useWindowEvent(\"keydown\", e => {\n if (e.key === \"Escape\") {\n onClose?.();\n e.preventDefault();\n e.stopPropagation();\n }\n });\n\n return (\n <>\n {triggerComponent}\n {isOpen && (\n <div\n className={containerClasses}\n ref={setFloatingRef}\n style={floatingStyles}\n {...getFloatingProps()}\n >\n <div className=\"mobius-popover\">\n <header className=\"mobius-popover__header\">\n <Button\n type=\"button\"\n className=\"mobius-popover__close-button\"\n onClick={toggleVisibility}\n aria-label=\"Close\"\n variant=\"ghost\"\n >\n <Icon\n icon={cross}\n size=\"md\"\n className=\"mobius-popover__close-icon\"\n />\n </Button>\n </header>\n <div className=\"mobius-popover__body\">{children}</div>\n </div>\n <FloatingArrow\n ref={arrowRef}\n context={context}\n width={20}\n className=\"mobius-popover__arrow-icon\"\n />\n </div>\n )}\n </>\n );\n};\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|