@nascentdigital/funnel-ui 4.5.2 → 4.5.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/dist/client/hooks/breakpoint/BreakpointContainer.d.ts +1 -1
- package/dist/client/hooks/breakpoint/BreakpointContainer.js +3 -1
- package/dist/client/interaction/index.d.ts +0 -6
- package/dist/client/interaction/index.js +0 -6
- package/dist/server/blocks/Block.js +2 -2
- package/dist/server/blocks/Message.js +2 -1
- package/dist/server/blocks/Testimonial.js +13 -10
- package/dist/server/blocks/Testimonials.d.ts +35 -35
- package/dist/server/blocks/Testimonials.js +21 -66
- package/dist/server/blocks/index.d.ts +1 -0
- package/dist/server/blocks/index.js +1 -0
- package/dist/server/fields/CallToActions.d.ts +12 -0
- package/dist/server/fields/CallToActions.js +27 -0
- package/dist/server/fields/index.d.ts +1 -0
- package/dist/server/fields/index.js +1 -0
- package/dist/server/panels/BlockPanel.js +1 -2
- package/dist/server/panels/MessagePanel.d.ts +2 -6
- package/dist/server/panels/MessagePanel.js +2 -9
- package/dist/server/panels/TestimonialPanel.d.ts +44 -18
- package/dist/server/panels/TestimonialPanel.js +44 -19
- package/dist/server/panels/VisualPanel.d.ts +1 -1
- package/package.json +2 -2
- package/dist/client/interaction/Popup.d.ts +0 -36
- package/dist/client/interaction/Popup.js +0 -244
- package/dist/client/interaction/Tooltip.d.ts +0 -16
- package/dist/client/interaction/Tooltip.js +0 -122
- package/dist/client/interaction/UILevel.d.ts +0 -1
- package/dist/client/interaction/UILevel.js +0 -2
- package/dist/client/interaction/backdrop/BackdropContainer.d.ts +0 -5
- package/dist/client/interaction/backdrop/BackdropContainer.js +0 -114
- package/dist/client/interaction/backdrop/BackdropContext.d.ts +0 -8
- package/dist/client/interaction/backdrop/BackdropContext.js +0 -6
- package/dist/client/interaction/backdrop/BackdropEvent.d.ts +0 -64
- package/dist/client/interaction/backdrop/BackdropEvent.js +0 -92
- package/dist/client/interaction/backdrop/index.d.ts +0 -4
- package/dist/client/interaction/backdrop/index.js +0 -20
- package/dist/client/interaction/backdrop/useBackdrop.d.ts +0 -13
- package/dist/client/interaction/backdrop/useBackdrop.js +0 -58
- package/dist/client/interaction/backdrop/useBackdropClick.d.ts +0 -2
- package/dist/client/interaction/backdrop/useBackdropClick.js +0 -21
- package/dist/client/interaction/backdrop/useBackdropVisible.d.ts +0 -1
- package/dist/client/interaction/backdrop/useBackdropVisible.js +0 -30
- package/dist/client/interaction/focus/FocusArea.d.ts +0 -10
- package/dist/client/interaction/focus/FocusArea.js +0 -38
- package/dist/client/interaction/focus/FocusContext.d.ts +0 -7
- package/dist/client/interaction/focus/FocusContext.js +0 -7
- package/dist/client/interaction/focus/FocusController.d.ts +0 -9
- package/dist/client/interaction/focus/FocusController.js +0 -40
- package/dist/client/interaction/focus/FocusService.d.ts +0 -20
- package/dist/client/interaction/focus/FocusService.js +0 -204
- package/dist/client/interaction/focus/LinkedList.d.ts +0 -18
- package/dist/client/interaction/focus/LinkedList.js +0 -122
- package/dist/client/interaction/focus/index.d.ts +0 -2
- package/dist/client/interaction/focus/index.js +0 -18
- package/dist/client/interaction/focus/useFocusService.d.ts +0 -2
- package/dist/client/interaction/focus/useFocusService.js +0 -14
- package/dist/client/interaction/modal/Modal.d.ts +0 -29
- package/dist/client/interaction/modal/Modal.js +0 -136
- package/dist/client/interaction/modal/ModalContainer.d.ts +0 -5
- package/dist/client/interaction/modal/ModalContainer.js +0 -104
- package/dist/client/interaction/modal/ModalContext.d.ts +0 -8
- package/dist/client/interaction/modal/ModalContext.js +0 -6
- package/dist/client/interaction/modal/ModalEvent.d.ts +0 -39
- package/dist/client/interaction/modal/ModalEvent.js +0 -53
- package/dist/client/interaction/modal/index.d.ts +0 -2
- package/dist/client/interaction/modal/index.js +0 -18
|
@@ -8,7 +8,7 @@ export declare namespace VisualPanel {
|
|
|
8
8
|
type Props = BlockPanel.Props & {
|
|
9
9
|
className?: string;
|
|
10
10
|
panel: BlockLayout.Visual;
|
|
11
|
-
visual
|
|
11
|
+
visual?: Visual.Model;
|
|
12
12
|
visualDecorator?: ReactNode;
|
|
13
13
|
visualLoading?: Visual.Loading;
|
|
14
14
|
aspect?: AspectRatio;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nascentdigital/funnel-ui",
|
|
3
3
|
"description": "UI components for funnel",
|
|
4
|
-
"version": "4.5.
|
|
4
|
+
"version": "4.5.3",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/",
|
|
7
7
|
"access": "restricted"
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"react-dom": "^19",
|
|
71
71
|
"ts-jest": "^29.3.2"
|
|
72
72
|
},
|
|
73
|
-
"gitHead": "
|
|
73
|
+
"gitHead": "ef42bb5440cc23187aeefc29319875ed5402676f"
|
|
74
74
|
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { Callback, Frame, Rect } from '@nascentdigital/funnel-core';
|
|
2
|
-
import { Variants } from 'framer-motion';
|
|
3
|
-
import { HTMLAttributes } from 'react';
|
|
4
|
-
import { ClickObserver } from './ClickObserver';
|
|
5
|
-
import { UILevel } from './UILevel';
|
|
6
|
-
export declare function Popup({ className, style, anchor, level, placement, anchorOffset, viewportPadding, animation, dismissTrigger, disableBackground, disabled, bubbleCloseEvent, open, onOpened: onOpenedRaw, onClose, onClosed: onClosedRaw, children, ...props }: Popup.Props): import("react/jsx-runtime").JSX.Element | null;
|
|
7
|
-
export declare namespace Popup {
|
|
8
|
-
type Props = HTMLAttributes<HTMLDivElement> & {
|
|
9
|
-
anchor?: Rect | Element | string | null | undefined;
|
|
10
|
-
level?: UILevel;
|
|
11
|
-
placement?: Placement;
|
|
12
|
-
anchorOffset?: number;
|
|
13
|
-
viewportPadding?: number;
|
|
14
|
-
animation?: Animation;
|
|
15
|
-
dismissTrigger?: ClickObserver.Trigger | 'none';
|
|
16
|
-
disableBackground?: boolean;
|
|
17
|
-
disabled?: boolean;
|
|
18
|
-
bubbleCloseEvent?: boolean;
|
|
19
|
-
open?: boolean;
|
|
20
|
-
onOpened?: Callback.Void;
|
|
21
|
-
onClose?: Callback.Void;
|
|
22
|
-
onClosed?: Callback.Void;
|
|
23
|
-
};
|
|
24
|
-
type Placement = 'top-start' | 'top' | 'top-end' | 'top-match' | 'bottom-start' | 'bottom' | 'bottom-end' | 'bottom-match' | 'left-start' | 'left' | 'left-end' | 'right-start' | 'right' | 'right-end';
|
|
25
|
-
type Animation = 'in' | 'out' | 'in-out';
|
|
26
|
-
type GetFrameArgs = {
|
|
27
|
-
anchorRect: Rect;
|
|
28
|
-
contentElement: HTMLDivElement;
|
|
29
|
-
placement: Popup.Placement;
|
|
30
|
-
anchorOffset: number;
|
|
31
|
-
viewportPadding: number;
|
|
32
|
-
};
|
|
33
|
-
function getFrame({ anchorRect, contentElement, placement, anchorOffset, viewportPadding }: GetFrameArgs): Partial<Frame>;
|
|
34
|
-
}
|
|
35
|
-
export declare const animations: Variants;
|
|
36
|
-
export declare function getAnchorRect(anchor: Rect | Element | string | null | undefined): Rect | null;
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
3
|
-
var t = {};
|
|
4
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
5
|
-
t[p] = s[p];
|
|
6
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
7
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
8
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
9
|
-
t[p[i]] = s[p[i]];
|
|
10
|
-
}
|
|
11
|
-
return t;
|
|
12
|
-
};
|
|
13
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.animations = void 0;
|
|
18
|
-
exports.Popup = Popup;
|
|
19
|
-
exports.getAnchorRect = getAnchorRect;
|
|
20
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
21
|
-
const funnel_core_1 = require("@nascentdigital/funnel-core");
|
|
22
|
-
const classnames_1 = __importDefault(require("classnames"));
|
|
23
|
-
const framer_motion_1 = require("framer-motion");
|
|
24
|
-
const react_1 = require("react");
|
|
25
|
-
const react_dom_1 = require("react-dom");
|
|
26
|
-
const react_hotkeys_hook_1 = require("react-hotkeys-hook");
|
|
27
|
-
const hooks_1 = require("../hooks");
|
|
28
|
-
const ClickObserver_1 = require("./ClickObserver");
|
|
29
|
-
// component
|
|
30
|
-
function Popup(_a) {
|
|
31
|
-
var { className, style = {}, anchor, level = 'l1', placement = 'bottom', anchorOffset = 0, viewportPadding = 0, animation = 'in-out', dismissTrigger, disableBackground = false, disabled = false, bubbleCloseEvent = false, open = false, onOpened: onOpenedRaw = funnel_core_1.Fn.NoOp, onClose = funnel_core_1.Fn.NoOp, onClosed: onClosedRaw = funnel_core_1.Fn.NoOp, children } = _a, props = __rest(_a, ["className", "style", "anchor", "level", "placement", "anchorOffset", "viewportPadding", "animation", "dismissTrigger", "disableBackground", "disabled", "bubbleCloseEvent", "open", "onOpened", "onClose", "onClosed", "children"]);
|
|
32
|
-
// bind context
|
|
33
|
-
const [active, setActive] = (0, react_1.useState)(false);
|
|
34
|
-
const openedRef = (0, react_1.useRef)(false);
|
|
35
|
-
const contentRef = (0, react_1.useRef)(null);
|
|
36
|
-
const id = (0, react_1.useId)();
|
|
37
|
-
const anchorRect = getAnchorRect(anchor);
|
|
38
|
-
const onOpenedRef = (0, react_1.useRef)(onOpenedRaw);
|
|
39
|
-
onOpenedRef.current = onOpenedRaw;
|
|
40
|
-
const onClosedRef = (0, react_1.useRef)(onClosedRaw);
|
|
41
|
-
onClosedRef.current = onClosedRaw;
|
|
42
|
-
const closeCallback = (0, react_1.useCallback)(() => {
|
|
43
|
-
if (openedRef.current) {
|
|
44
|
-
onClose();
|
|
45
|
-
}
|
|
46
|
-
}, [onClose]);
|
|
47
|
-
// re-render on window size change
|
|
48
|
-
(0, hooks_1.useViewportSize)();
|
|
49
|
-
// close on escape
|
|
50
|
-
(0, react_hotkeys_hook_1.useHotkeys)('esc', closeCallback);
|
|
51
|
-
// show/hide based on open change
|
|
52
|
-
(0, react_1.useEffect)(() => {
|
|
53
|
-
// raise event on open/close
|
|
54
|
-
let unlock = undefined;
|
|
55
|
-
if (open) {
|
|
56
|
-
// update ref
|
|
57
|
-
openedRef.current = true;
|
|
58
|
-
// activate
|
|
59
|
-
setActive(true);
|
|
60
|
-
// lock keyboard
|
|
61
|
-
unlock = funnel_core_1.Browser.lockFocus(`#${CSS.escape(id)}`);
|
|
62
|
-
}
|
|
63
|
-
// raise event on close (if it was actually opened)
|
|
64
|
-
else if (openedRef.current) {
|
|
65
|
-
// deactivate
|
|
66
|
-
setActive(false);
|
|
67
|
-
// update ref
|
|
68
|
-
openedRef.current = false;
|
|
69
|
-
}
|
|
70
|
-
// on unmount
|
|
71
|
-
return () => {
|
|
72
|
-
// unlock (if locked)
|
|
73
|
-
if (unlock) {
|
|
74
|
-
unlock();
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
}, [open, id]);
|
|
78
|
-
// skip for SSR
|
|
79
|
-
if (!funnel_core_1.Browser.available) {
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
// compute popup rect (if visible + ready)
|
|
83
|
-
const contentElement = contentRef.current;
|
|
84
|
-
const popupFrame = active && !!anchorRect && !!contentElement
|
|
85
|
-
? Popup.getFrame({
|
|
86
|
-
anchorRect,
|
|
87
|
-
contentElement,
|
|
88
|
-
placement,
|
|
89
|
-
anchorOffset,
|
|
90
|
-
viewportPadding
|
|
91
|
-
})
|
|
92
|
-
: {};
|
|
93
|
-
// helpers
|
|
94
|
-
const onClickOutside = (e) => {
|
|
95
|
-
// consume click
|
|
96
|
-
if (!bubbleCloseEvent) {
|
|
97
|
-
e.preventDefault();
|
|
98
|
-
e.stopPropagation();
|
|
99
|
-
}
|
|
100
|
-
// close
|
|
101
|
-
onClose();
|
|
102
|
-
};
|
|
103
|
-
const onAnimationComplete = (definition) => {
|
|
104
|
-
if (definition === 'visible') {
|
|
105
|
-
onOpenedRef.current();
|
|
106
|
-
}
|
|
107
|
-
else if (definition === 'exit') {
|
|
108
|
-
onClosedRef.current();
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
// render
|
|
112
|
-
return ((0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { children: open && !!anchorRect &&
|
|
113
|
-
(0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { id: id, className: (0, classnames_1.default)('Popup-wrap', {
|
|
114
|
-
'Popup-disableBg': disableBackground,
|
|
115
|
-
'Popup-disabled': disabled,
|
|
116
|
-
'Popup-l1': level === 'l1',
|
|
117
|
-
'Popup-l2': level === 'l2',
|
|
118
|
-
'Popup-l3': level === 'l3'
|
|
119
|
-
}), variants: exports.animations, initial: animation !== 'out' ? 'hidden' : 'visible', animate: "visible", exit: animation !== 'in' ? 'exit' : 'visible', onAnimationComplete: definition => onAnimationComplete(definition), children: (0, jsx_runtime_1.jsx)(ClickObserver_1.ClickObserver, { trigger: dismissTrigger === 'none' ? 'click' : dismissTrigger, disabled: !open || dismissTrigger === 'none', onClickOutside: onClickOutside, children: (0, jsx_runtime_1.jsx)("div", Object.assign({}, props, { ref: contentRef, style: Object.assign(Object.assign({}, style), popupFrame), className: (0, classnames_1.default)(className, 'Popup-root'), children: children })) }) }, id) }), document.body) }));
|
|
120
|
-
}
|
|
121
|
-
// extension
|
|
122
|
-
(function (Popup) {
|
|
123
|
-
// helpers
|
|
124
|
-
function getFrame({ anchorRect, contentElement, placement, anchorOffset, viewportPadding }) {
|
|
125
|
-
// get geometries
|
|
126
|
-
const viewportRect = new funnel_core_1.Rect(viewportPadding, viewportPadding, window.innerWidth - viewportPadding * 2, window.innerHeight - viewportPadding * 2);
|
|
127
|
-
const contentWidth = contentElement.offsetWidth;
|
|
128
|
-
const contentHeight = contentElement.offsetHeight;
|
|
129
|
-
// horizontal layout if left/right
|
|
130
|
-
const popupFrame = {};
|
|
131
|
-
const [primary, secondary] = placement.split('-');
|
|
132
|
-
if (primary === 'left' || primary === 'right') {
|
|
133
|
-
// determine space to left/right
|
|
134
|
-
const spaceLeft = Math.max(0, anchorRect.left - viewportRect.left - anchorOffset);
|
|
135
|
-
const spaceRight = Math.max(0, viewportRect.right - anchorRect.right - anchorOffset);
|
|
136
|
-
// place left if it fits or it fits better than on the right
|
|
137
|
-
if ((primary === 'left' && (spaceLeft >= contentWidth || spaceLeft > spaceRight))
|
|
138
|
-
|| (primary === 'right' && spaceRight < contentWidth && spaceRight < spaceLeft)) {
|
|
139
|
-
popupFrame.right = viewportRect.right - anchorRect.left + anchorOffset;
|
|
140
|
-
}
|
|
141
|
-
// or place right
|
|
142
|
-
else {
|
|
143
|
-
popupFrame.left = anchorRect.right + anchorOffset;
|
|
144
|
-
}
|
|
145
|
-
// top-align on start
|
|
146
|
-
if (secondary === 'start') {
|
|
147
|
-
popupFrame.top = anchorRect.top;
|
|
148
|
-
}
|
|
149
|
-
// bottom-align on end
|
|
150
|
-
else if (secondary === 'end') {
|
|
151
|
-
popupFrame.bottom = viewportRect.bottom - anchorRect.bottom;
|
|
152
|
-
}
|
|
153
|
-
// or center against anchor
|
|
154
|
-
else {
|
|
155
|
-
// set top bounds so it's centered
|
|
156
|
-
popupFrame.top = Math.max(anchorRect.top + (anchorRect.height - contentHeight) / 2, viewportRect.top);
|
|
157
|
-
// offset if the bottom is bleeding over
|
|
158
|
-
if (popupFrame.top + contentHeight > viewportRect.bottom) {
|
|
159
|
-
popupFrame.top -= popupFrame.top + contentHeight - viewportRect.bottom;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
// or standard vertical layout
|
|
164
|
-
else {
|
|
165
|
-
// determine space to top/bottom
|
|
166
|
-
const spaceTop = Math.max(0, anchorRect.top - viewportRect.top - anchorOffset);
|
|
167
|
-
const spaceBottom = Math.max(0, viewportRect.bottom - anchorRect.bottom - anchorOffset);
|
|
168
|
-
// place above if it fits or it fits better than on the right
|
|
169
|
-
if ((primary === 'top' && (spaceTop >= contentHeight || spaceBottom < spaceTop))
|
|
170
|
-
|| (primary === 'bottom' && spaceBottom < contentHeight && spaceBottom < spaceTop)) {
|
|
171
|
-
popupFrame.bottom = viewportRect.bottom - anchorRect.top + anchorOffset;
|
|
172
|
-
}
|
|
173
|
-
// or place below
|
|
174
|
-
else {
|
|
175
|
-
popupFrame.top = anchorRect.bottom + anchorOffset;
|
|
176
|
-
}
|
|
177
|
-
// left-align on start
|
|
178
|
-
if (secondary === 'start') {
|
|
179
|
-
popupFrame.left = anchorRect.left;
|
|
180
|
-
}
|
|
181
|
-
// right-align on end
|
|
182
|
-
else if (secondary === 'end') {
|
|
183
|
-
popupFrame.right = viewportRect.right - anchorRect.right;
|
|
184
|
-
}
|
|
185
|
-
// match width
|
|
186
|
-
else if (secondary === 'match') {
|
|
187
|
-
popupFrame.left = anchorRect.left;
|
|
188
|
-
popupFrame.width = anchorRect.width;
|
|
189
|
-
}
|
|
190
|
-
// or center against anchor
|
|
191
|
-
else {
|
|
192
|
-
// set left bounds so it's centered
|
|
193
|
-
popupFrame.left = Math.max(anchorRect.left + (anchorRect.width - contentWidth) / 2, viewportRect.left);
|
|
194
|
-
// offset if the right is bleeding over
|
|
195
|
-
if (popupFrame.left + contentWidth > viewportRect.right) {
|
|
196
|
-
popupFrame.left -= popupFrame.left + contentWidth - viewportRect.right;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
// return position insets
|
|
201
|
-
return popupFrame;
|
|
202
|
-
}
|
|
203
|
-
Popup.getFrame = getFrame;
|
|
204
|
-
})(Popup || (exports.Popup = Popup = {}));
|
|
205
|
-
// helpers
|
|
206
|
-
exports.animations = {
|
|
207
|
-
hidden: {
|
|
208
|
-
opacity: 0
|
|
209
|
-
},
|
|
210
|
-
visible: {
|
|
211
|
-
opacity: 1,
|
|
212
|
-
transition: {
|
|
213
|
-
ease: 'easeOut',
|
|
214
|
-
duration: 0.5,
|
|
215
|
-
when: 'beforeChildren'
|
|
216
|
-
}
|
|
217
|
-
},
|
|
218
|
-
exit: {
|
|
219
|
-
opacity: 0,
|
|
220
|
-
transition: {
|
|
221
|
-
ease: 'easeIn',
|
|
222
|
-
duration: 0.15,
|
|
223
|
-
bounce: 0,
|
|
224
|
-
when: 'afterChildren'
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
};
|
|
228
|
-
function getAnchorRect(anchor) {
|
|
229
|
-
// skip if there's no anchor or if we're SSR
|
|
230
|
-
if (!anchor || !funnel_core_1.Browser.available) {
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
// or skip if we already have a rect
|
|
234
|
-
else if (anchor instanceof funnel_core_1.Rect) {
|
|
235
|
-
return anchor;
|
|
236
|
-
}
|
|
237
|
-
// try to coerce anchor into an element (or fail)
|
|
238
|
-
const anchorElement = typeof anchor === 'string'
|
|
239
|
-
? document.querySelector(anchor)
|
|
240
|
-
: anchor;
|
|
241
|
-
return anchorElement
|
|
242
|
-
? funnel_core_1.Rect.fromInsets(anchorElement.getBoundingClientRect())
|
|
243
|
-
: null;
|
|
244
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { ReactNode, RefObject } from 'react';
|
|
2
|
-
import { Popup } from './Popup';
|
|
3
|
-
export declare function Tooltip({ delay, scroller, content, children, anchorClassName, popupClassName, onClose, placement, anchorOffset, viewportPadding, disabled }: DelayedPopup.Props): import("react/jsx-runtime").JSX.Element;
|
|
4
|
-
export declare namespace DelayedPopup {
|
|
5
|
-
type Props = Pick<Popup.Props, 'anchorOffset' | 'viewportPadding' | 'placement' | 'onClose'> & {
|
|
6
|
-
delay?: number;
|
|
7
|
-
scroller?: string | RefObject<HTMLElement | null>;
|
|
8
|
-
content: ReactNode;
|
|
9
|
-
children: ReactNode;
|
|
10
|
-
anchorClassName?: string;
|
|
11
|
-
popupClassName?: string;
|
|
12
|
-
disabled?: boolean;
|
|
13
|
-
};
|
|
14
|
-
type HoverState = 'inactive' | 'waiting' | 'active';
|
|
15
|
-
type TapState = 'inactive' | 'active';
|
|
16
|
-
}
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.Tooltip = Tooltip;
|
|
7
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
-
const funnel_core_1 = require("@nascentdigital/funnel-core");
|
|
9
|
-
const classnames_1 = __importDefault(require("classnames"));
|
|
10
|
-
const framer_motion_1 = require("framer-motion");
|
|
11
|
-
const react_1 = require("react");
|
|
12
|
-
const react_dom_1 = require("react-dom");
|
|
13
|
-
const rxjs_1 = require("rxjs");
|
|
14
|
-
const hooks_1 = require("../hooks");
|
|
15
|
-
const ClickObserver_1 = require("./ClickObserver");
|
|
16
|
-
const Popup_1 = require("./Popup");
|
|
17
|
-
// component
|
|
18
|
-
function Tooltip({ delay = 200, scroller, content, children, anchorClassName, popupClassName, onClose = funnel_core_1.Fn.NoOp, placement = 'bottom', anchorOffset = 0, viewportPadding = 0, disabled = false }) {
|
|
19
|
-
// bind state + hooks
|
|
20
|
-
const id = (0, react_1.useId)();
|
|
21
|
-
const [hoverState, setHoverState] = (0, react_1.useState)('inactive');
|
|
22
|
-
const [tapState, setTapState] = (0, react_1.useState)('inactive');
|
|
23
|
-
const [anchorRef, anchorElement] = (0, hooks_1.useRefElement)();
|
|
24
|
-
const [contentRef, contentElement] = (0, hooks_1.useRefElement)();
|
|
25
|
-
const scrollerRef = (0, hooks_1.useElementSelector)(scroller);
|
|
26
|
-
const scrollOffsetRef = (0, react_1.useRef)(undefined);
|
|
27
|
-
// only pass scroller ref if a scroller element was provided in props
|
|
28
|
-
const { scrollY } = (0, hooks_1.useElementScrolled)(scroller ? scrollerRef : undefined);
|
|
29
|
-
// set open if either hover state or tap state are active and not disabled
|
|
30
|
-
const open = (0, react_1.useMemo)(() => {
|
|
31
|
-
return (hoverState === 'active' || tapState === 'active') && !disabled;
|
|
32
|
-
}, [hoverState, tapState, disabled]);
|
|
33
|
-
// state for: hover [inactive, waiting, active] (mouse focus), tap [inactive, active] (pointer down)
|
|
34
|
-
// hooks for: window resize, scroll, on-window-click -> dismiss / deactivate anything
|
|
35
|
-
// helpers
|
|
36
|
-
const onOpen = (0, react_1.useCallback)(() => {
|
|
37
|
-
var _a;
|
|
38
|
-
// when tooltip is opened, set initial scroll offset
|
|
39
|
-
scrollOffsetRef.current = (_a = scrollY === null || scrollY === void 0 ? void 0 : scrollY.offset) !== null && _a !== void 0 ? _a : 0;
|
|
40
|
-
}, [scrollOffsetRef, scrollY]);
|
|
41
|
-
// wire on mouse enter
|
|
42
|
-
const onPointerEnter = (0, react_1.useCallback)((e) => {
|
|
43
|
-
// ignore event if it's not a mouse event
|
|
44
|
-
if (e.pointerType !== 'mouse') {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
// set hover state to waiting
|
|
48
|
-
setHoverState('waiting');
|
|
49
|
-
// delay opening popup
|
|
50
|
-
(0, rxjs_1.timer)(delay).subscribe(() => setHoverState(prevState => {
|
|
51
|
-
// if current state is still waiting, set to active and call onOpen
|
|
52
|
-
if (prevState === 'waiting') {
|
|
53
|
-
onOpen();
|
|
54
|
-
return 'active';
|
|
55
|
-
}
|
|
56
|
-
// if prevState is not waiting, set to inactive
|
|
57
|
-
else {
|
|
58
|
-
return 'inactive';
|
|
59
|
-
}
|
|
60
|
-
}));
|
|
61
|
-
}, [onOpen, delay]);
|
|
62
|
-
// wire on mouse leave
|
|
63
|
-
const onPointerLeave = (0, react_1.useCallback)((e) => {
|
|
64
|
-
// ignore event if it's not a mouse event
|
|
65
|
-
if (e.pointerType !== 'mouse') {
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
// update hover state
|
|
69
|
-
setHoverState('inactive');
|
|
70
|
-
onClose();
|
|
71
|
-
}, [onClose]);
|
|
72
|
-
// wire tap on mobile
|
|
73
|
-
const onClick = (0, react_1.useCallback)((e) => {
|
|
74
|
-
// ignore mouse click - only hover should be triggered when mouse is present
|
|
75
|
-
if ('pointerType' in e && e.pointerType === 'mouse') {
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
// update tap state and call onOpen
|
|
79
|
-
setTapState('active');
|
|
80
|
-
onOpen();
|
|
81
|
-
}, [onOpen]);
|
|
82
|
-
// dismiss popup
|
|
83
|
-
const dismiss = (0, react_1.useCallback)(() => {
|
|
84
|
-
// update hover and tap state to inactive
|
|
85
|
-
setHoverState('inactive');
|
|
86
|
-
setTapState('inactive');
|
|
87
|
-
onClose();
|
|
88
|
-
}, [onClose]);
|
|
89
|
-
// listen for scroll changes
|
|
90
|
-
(0, react_1.useEffect)(() => {
|
|
91
|
-
// if tooltip is open and scroll offset has changed, dismiss tooltip
|
|
92
|
-
const hasScrolled = scrollOffsetRef.current !== undefined && (scrollY === null || scrollY === void 0 ? void 0 : scrollY.offset) !== undefined
|
|
93
|
-
&& scrollOffsetRef.current !== (scrollY === null || scrollY === void 0 ? void 0 : scrollY.offset);
|
|
94
|
-
if (open && hasScrolled) {
|
|
95
|
-
dismiss();
|
|
96
|
-
}
|
|
97
|
-
}, [open, scrollY, dismiss]);
|
|
98
|
-
// bind hover and click listeners
|
|
99
|
-
(0, react_1.useEffect)(() => {
|
|
100
|
-
anchorElement === null || anchorElement === void 0 ? void 0 : anchorElement.addEventListener('pointerenter', onPointerEnter);
|
|
101
|
-
anchorElement === null || anchorElement === void 0 ? void 0 : anchorElement.addEventListener('pointerleave', onPointerLeave);
|
|
102
|
-
anchorElement === null || anchorElement === void 0 ? void 0 : anchorElement.addEventListener('click', onClick);
|
|
103
|
-
// unbind listeners
|
|
104
|
-
return () => {
|
|
105
|
-
anchorElement === null || anchorElement === void 0 ? void 0 : anchorElement.removeEventListener('pointerenter', onPointerEnter);
|
|
106
|
-
anchorElement === null || anchorElement === void 0 ? void 0 : anchorElement.removeEventListener('pointerleave', onPointerLeave);
|
|
107
|
-
anchorElement === null || anchorElement === void 0 ? void 0 : anchorElement.removeEventListener('click', onClick);
|
|
108
|
-
};
|
|
109
|
-
}, [anchorElement, onPointerEnter, onPointerLeave, onClick]);
|
|
110
|
-
const anchorRect = (0, Popup_1.getAnchorRect)(anchorElement);
|
|
111
|
-
const popupFrame = !!anchorRect && !!contentElement
|
|
112
|
-
? Popup_1.Popup.getFrame({
|
|
113
|
-
anchorRect,
|
|
114
|
-
contentElement,
|
|
115
|
-
placement,
|
|
116
|
-
anchorOffset,
|
|
117
|
-
viewportPadding
|
|
118
|
-
}) : {};
|
|
119
|
-
// render
|
|
120
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("div", { className: anchorClassName, ref: anchorRef, children: children }), funnel_core_1.Browser.available && (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { children: open && !!anchorElement &&
|
|
121
|
-
(0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { id: id, className: "fixed inset-0 z-popup pointer-events-none", initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { ease: 'circIn', duration: 0.2 }, children: (0, jsx_runtime_1.jsx)(ClickObserver_1.ClickObserver, { disabled: !open, onClickOutside: dismiss, children: (0, jsx_runtime_1.jsx)("div", { ref: contentRef, style: popupFrame, className: (0, classnames_1.default)(popupClassName, 'absolute pointer-events-auto'), children: content }) }) }, id) }), document.body)] }));
|
|
122
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export type UILevel = 'l1' | 'l2' | 'l3';
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.BackdropContainer = BackdropContainer;
|
|
7
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
-
const funnel_core_1 = require("@nascentdigital/funnel-core");
|
|
9
|
-
const classnames_1 = __importDefault(require("classnames"));
|
|
10
|
-
const framer_motion_1 = require("framer-motion");
|
|
11
|
-
const react_1 = require("react");
|
|
12
|
-
const react_dom_1 = require("react-dom");
|
|
13
|
-
const rxjs_1 = require("rxjs");
|
|
14
|
-
const BackdropContext_1 = require("./BackdropContext");
|
|
15
|
-
const BackdropEvent_1 = require("./BackdropEvent");
|
|
16
|
-
// component
|
|
17
|
-
function BackdropContainer({ children }) {
|
|
18
|
-
var _a, _b;
|
|
19
|
-
// bind context
|
|
20
|
-
const [dependantId, setDependantId] = (0, react_1.useState)();
|
|
21
|
-
const dependantsRef = (0, react_1.useRef)([]);
|
|
22
|
-
const dependants = dependantsRef.current;
|
|
23
|
-
const context = (0, react_1.useMemo)(() => ({
|
|
24
|
-
eventBus: new rxjs_1.Subject()
|
|
25
|
-
}), []);
|
|
26
|
-
const { eventBus } = context;
|
|
27
|
-
const backdropOptions = (_b = (_a = dependants.find(ctx => ctx.id === dependantId)) === null || _a === void 0 ? void 0 : _a.options) !== null && _b !== void 0 ? _b : {};
|
|
28
|
-
// listen to events from dependants
|
|
29
|
-
(0, react_1.useEffect)(() => {
|
|
30
|
-
// subscribe to events, proxying changes to backdrop
|
|
31
|
-
const subscription = eventBus
|
|
32
|
-
.pipe((0, rxjs_1.filter)(e => BackdropEvent_1.BackdropEvent.Capture.guard(e) || BackdropEvent_1.BackdropEvent.Release.guard(e)))
|
|
33
|
-
.subscribe(event => {
|
|
34
|
-
// handle capture
|
|
35
|
-
const dependants = dependantsRef.current;
|
|
36
|
-
const { id } = event;
|
|
37
|
-
if (BackdropEvent_1.BackdropEvent.Capture.guard(event)) {
|
|
38
|
-
// exit if dependant already exists in list
|
|
39
|
-
if (dependants.some(ctx => ctx.id === id)) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
// add new dependant
|
|
43
|
-
dependants.push({
|
|
44
|
-
id,
|
|
45
|
-
options: event.options
|
|
46
|
-
});
|
|
47
|
-
// set dependant to first in active list
|
|
48
|
-
setDependantId(id);
|
|
49
|
-
// raise event
|
|
50
|
-
eventBus.next(BackdropEvent_1.BackdropEvent.Show.create(dependants.map(ctx => ctx.id), id, event.options));
|
|
51
|
-
}
|
|
52
|
-
// handle release
|
|
53
|
-
else if (BackdropEvent_1.BackdropEvent.Release.guard(event)) {
|
|
54
|
-
// determine index (or exit if not found)
|
|
55
|
-
const dependantIndex = dependants.findIndex(ctx => ctx.id === id);
|
|
56
|
-
if (dependantIndex < 0) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
// remove dependant from list
|
|
60
|
-
dependants.splice(dependantIndex, 1);
|
|
61
|
-
// reactivate previous dependant (if any)
|
|
62
|
-
const dependantCount = dependants.length;
|
|
63
|
-
const nextDependant = dependantCount > 0
|
|
64
|
-
? dependants[dependantCount - 1]
|
|
65
|
-
: undefined;
|
|
66
|
-
if (nextDependant) {
|
|
67
|
-
eventBus.next(BackdropEvent_1.BackdropEvent.FocusChange.create(nextDependant.id));
|
|
68
|
-
setDependantId(nextDependant.id);
|
|
69
|
-
}
|
|
70
|
-
// or unlock ui
|
|
71
|
-
else {
|
|
72
|
-
eventBus.next(BackdropEvent_1.BackdropEvent.Hide.create());
|
|
73
|
-
setDependantId(undefined);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
// unmount
|
|
78
|
-
return () => subscription.unsubscribe();
|
|
79
|
-
}, [eventBus]);
|
|
80
|
-
// handle lock/unlock of ui
|
|
81
|
-
(0, react_1.useEffect)(() => {
|
|
82
|
-
// handle ui lock if we have a dependant
|
|
83
|
-
let unlock = undefined;
|
|
84
|
-
if (dependantId) {
|
|
85
|
-
unlock = funnel_core_1.Browser.lockFocus(`#${CSS.escape(dependantId)}`);
|
|
86
|
-
}
|
|
87
|
-
// unlock previous (if any)
|
|
88
|
-
return () => unlock && unlock();
|
|
89
|
-
}, [dependantId]);
|
|
90
|
-
// handle click
|
|
91
|
-
const onClick = () => {
|
|
92
|
-
// raise click event if we're open
|
|
93
|
-
if (dependantId) {
|
|
94
|
-
eventBus.next(BackdropEvent_1.BackdropEvent.Click.create());
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
// render
|
|
98
|
-
const { hideNav = false, level = 'l1' } = backdropOptions;
|
|
99
|
-
return ((0, jsx_runtime_1.jsxs)(BackdropContext_1.BackdropContext.Provider, { value: context, children: [children, funnel_core_1.Browser.available && (0, react_dom_1.createPortal)((0, jsx_runtime_1.jsx)(framer_motion_1.AnimatePresence, { mode: "wait", children: dependantId &&
|
|
100
|
-
(0, jsx_runtime_1.jsx)(framer_motion_1.motion.div, { className: (0, classnames_1.default)('Backdrop-root', {
|
|
101
|
-
'Backdrop-l1': !hideNav && level === 'l1',
|
|
102
|
-
'Backdrop-l2': hideNav || level === 'l2',
|
|
103
|
-
'Backdrop-l3': !hideNav && level === 'l3'
|
|
104
|
-
}), variants: animationVariants, initial: "invisible", animate: "visible", exit: "invisible", onClick: onClick }, "backdrop") }), document.body)] }));
|
|
105
|
-
}
|
|
106
|
-
// animations
|
|
107
|
-
const animationVariants = {
|
|
108
|
-
invisible: {
|
|
109
|
-
opacity: 0
|
|
110
|
-
},
|
|
111
|
-
visible: {
|
|
112
|
-
opacity: 1
|
|
113
|
-
}
|
|
114
|
-
};
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Subject } from 'rxjs';
|
|
2
|
-
import { BackdropEvent } from './BackdropEvent';
|
|
3
|
-
export declare const BackdropContext: import("react").Context<BackdropContext.Data>;
|
|
4
|
-
export declare namespace BackdropContext {
|
|
5
|
-
type Data = {
|
|
6
|
-
eventBus: Subject<BackdropEvent>;
|
|
7
|
-
};
|
|
8
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { Tagged } from 'type-fest';
|
|
2
|
-
import { UILevel } from '../UILevel';
|
|
3
|
-
export type BackdropEvent = BackdropEvent.Show | BackdropEvent.Hide | BackdropEvent.Click | BackdropEvent.FocusChange | BackdropEvent.Capture | BackdropEvent.Release;
|
|
4
|
-
export declare namespace BackdropEvent {
|
|
5
|
-
export type Action = 'show' | 'hide' | 'click' | 'focusChange' | 'capture' | 'release';
|
|
6
|
-
export type Options = {
|
|
7
|
-
hideNav?: boolean;
|
|
8
|
-
level?: UILevel;
|
|
9
|
-
};
|
|
10
|
-
export type DependantId = Tagged<string, 'funnel.BackdropEvent.DependantId'>;
|
|
11
|
-
interface Base {
|
|
12
|
-
action: Action;
|
|
13
|
-
}
|
|
14
|
-
export interface Show extends Base {
|
|
15
|
-
action: 'show';
|
|
16
|
-
dependent: DependantId;
|
|
17
|
-
dependants: ReadonlyArray<DependantId>;
|
|
18
|
-
options: Options;
|
|
19
|
-
}
|
|
20
|
-
export namespace Show {
|
|
21
|
-
function guard(event: BackdropEvent): event is Show;
|
|
22
|
-
function create(dependants: ReadonlyArray<DependantId>, dependent: DependantId, options: Options): Show;
|
|
23
|
-
}
|
|
24
|
-
export interface Hide extends Base {
|
|
25
|
-
action: 'hide';
|
|
26
|
-
}
|
|
27
|
-
export namespace Hide {
|
|
28
|
-
function guard(event: BackdropEvent): event is Hide;
|
|
29
|
-
function create(): Hide;
|
|
30
|
-
}
|
|
31
|
-
export interface Click extends Base {
|
|
32
|
-
action: 'click';
|
|
33
|
-
}
|
|
34
|
-
export namespace Click {
|
|
35
|
-
function guard(event: BackdropEvent): event is Click;
|
|
36
|
-
function create(): Click;
|
|
37
|
-
}
|
|
38
|
-
export interface FocusChange extends Base {
|
|
39
|
-
action: 'focusChange';
|
|
40
|
-
dependent: DependantId;
|
|
41
|
-
}
|
|
42
|
-
export namespace FocusChange {
|
|
43
|
-
function guard(event: BackdropEvent): event is FocusChange;
|
|
44
|
-
function create(dependent: DependantId): FocusChange;
|
|
45
|
-
}
|
|
46
|
-
export interface Capture extends Base {
|
|
47
|
-
id: DependantId;
|
|
48
|
-
action: 'capture';
|
|
49
|
-
options: Options;
|
|
50
|
-
}
|
|
51
|
-
export namespace Capture {
|
|
52
|
-
function guard(event: BackdropEvent): event is Capture;
|
|
53
|
-
function create(id: DependantId, options: Options): Capture;
|
|
54
|
-
}
|
|
55
|
-
export interface Release extends Base {
|
|
56
|
-
id: DependantId;
|
|
57
|
-
action: 'release';
|
|
58
|
-
}
|
|
59
|
-
export namespace Release {
|
|
60
|
-
function guard(event: BackdropEvent): event is Release;
|
|
61
|
-
function create(id: DependantId): Release;
|
|
62
|
-
}
|
|
63
|
-
export {};
|
|
64
|
-
}
|