@vuu-ui/vuu-popups 2.1.19-beta.2 → 2.1.19-beta.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/cjs/index.js +18 -0
- package/cjs/index.js.map +1 -0
- package/cjs/popup/Popup.css.js +6 -0
- package/cjs/popup/Popup.css.js.map +1 -0
- package/cjs/popup/Popup.js +38 -0
- package/cjs/popup/Popup.js.map +1 -0
- package/cjs/popup/getPositionRelativeToAnchor.js +43 -0
- package/cjs/popup/getPositionRelativeToAnchor.js.map +1 -0
- package/cjs/popup/useAnchoredPosition.js +58 -0
- package/cjs/popup/useAnchoredPosition.js.map +1 -0
- package/cjs/popup-menu/PopupMenu.css.js +6 -0
- package/cjs/popup-menu/PopupMenu.css.js.map +1 -0
- package/cjs/popup-menu/PopupMenu.js +100 -0
- package/cjs/popup-menu/PopupMenu.js.map +1 -0
- package/cjs/popup-menu/usePopupMenu.js +100 -0
- package/cjs/popup-menu/usePopupMenu.js.map +1 -0
- package/cjs/portal/Portal.css.js +6 -0
- package/cjs/portal/Portal.css.js.map +1 -0
- package/cjs/portal/Portal.js +73 -0
- package/cjs/portal/Portal.js.map +1 -0
- package/cjs/tooltip/Tooltip.css.js +6 -0
- package/cjs/tooltip/Tooltip.css.js.map +1 -0
- package/cjs/tooltip/Tooltip.js +56 -0
- package/cjs/tooltip/Tooltip.js.map +1 -0
- package/cjs/tooltip/useTooltip.js +106 -0
- package/cjs/tooltip/useTooltip.js.map +1 -0
- package/cjs/tooltip/useTooltipAnchoredPosition.js +117 -0
- package/cjs/tooltip/useTooltipAnchoredPosition.js.map +1 -0
- package/esm/index.js +7 -0
- package/esm/index.js.map +1 -0
- package/esm/popup/Popup.css.js +4 -0
- package/esm/popup/Popup.css.js.map +1 -0
- package/esm/popup/Popup.js +36 -0
- package/esm/popup/Popup.js.map +1 -0
- package/esm/popup/getPositionRelativeToAnchor.js +41 -0
- package/esm/popup/getPositionRelativeToAnchor.js.map +1 -0
- package/esm/popup/useAnchoredPosition.js +56 -0
- package/esm/popup/useAnchoredPosition.js.map +1 -0
- package/esm/popup-menu/PopupMenu.css.js +4 -0
- package/esm/popup-menu/PopupMenu.css.js.map +1 -0
- package/esm/popup-menu/PopupMenu.js +98 -0
- package/esm/popup-menu/PopupMenu.js.map +1 -0
- package/esm/popup-menu/usePopupMenu.js +98 -0
- package/esm/popup-menu/usePopupMenu.js.map +1 -0
- package/esm/portal/Portal.css.js +4 -0
- package/esm/portal/Portal.css.js.map +1 -0
- package/esm/portal/Portal.js +71 -0
- package/esm/portal/Portal.js.map +1 -0
- package/esm/tooltip/Tooltip.css.js +4 -0
- package/esm/tooltip/Tooltip.css.js.map +1 -0
- package/esm/tooltip/Tooltip.js +54 -0
- package/esm/tooltip/Tooltip.js.map +1 -0
- package/esm/tooltip/useTooltip.js +104 -0
- package/esm/tooltip/useTooltip.js.map +1 -0
- package/esm/tooltip/useTooltipAnchoredPosition.js +115 -0
- package/esm/tooltip/useTooltipAnchoredPosition.js.map +1 -0
- package/package.json +13 -10
- package/src/index.js +0 -4
- package/src/popup/Popup.js +0 -29
- package/src/popup/getPositionRelativeToAnchor.js +0 -47
- package/src/popup/index.js +0 -2
- package/src/popup/useAnchoredPosition.js +0 -41
- package/src/popup-menu/PopupMenu.css.js +0 -11
- package/src/popup-menu/PopupMenu.js +0 -67
- package/src/popup-menu/index.js +0 -1
- package/src/popup-menu/usePopupMenu.js +0 -72
- package/src/portal/Portal.css.js +0 -27
- package/src/portal/Portal.js +0 -62
- package/src/portal/index.js +0 -1
- package/src/tooltip/Tooltip.css.js +0 -76
- package/src/tooltip/Tooltip.js +0 -41
- package/src/tooltip/index.js +0 -2
- package/src/tooltip/useTooltip.js +0 -91
- package/src/tooltip/useTooltipAnchoredPosition.js +0 -102
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Button } from "@salt-ds/core";
|
|
3
|
-
import { useComponentCssInjection } from "@salt-ds/styles";
|
|
4
|
-
import { useWindow } from "@salt-ds/window";
|
|
5
|
-
import { Icon, IconButton } from "@vuu-ui/vuu-ui-controls";
|
|
6
|
-
import { useId } from "@vuu-ui/vuu-utils";
|
|
7
|
-
import clsx from "clsx";
|
|
8
|
-
import { usePopupMenu } from "./usePopupMenu.js";
|
|
9
|
-
import PopupMenu from "./PopupMenu.css";
|
|
10
|
-
const classBase = "vuuPopupMenu";
|
|
11
|
-
const PopupMenu_PopupMenu = ({ anchorElement, "aria-label": ariaLabel, appearance = "transparent", className, disabled = false, label, icon = label ? "chevron-down" : "more-vert", id: idProp, menuActionHandler, menuBuilder, menuClassName, menuLocation = "header", menuOptions, onMenuClose, onMenuOpen, popupPlacement = "below-right", sentiment = "neutral", tabIndex = 0, ...htmlAttributes })=>{
|
|
12
|
-
const targetWindow = useWindow();
|
|
13
|
-
useComponentCssInjection({
|
|
14
|
-
testId: "vuu-popup-menu",
|
|
15
|
-
css: PopupMenu,
|
|
16
|
-
window: targetWindow
|
|
17
|
-
});
|
|
18
|
-
const id = useId(idProp);
|
|
19
|
-
const { ariaAttributes, buttonProps, menuOpen, rootRef } = usePopupMenu({
|
|
20
|
-
anchorElement,
|
|
21
|
-
"aria-label": ariaLabel,
|
|
22
|
-
id,
|
|
23
|
-
menuActionHandler,
|
|
24
|
-
menuBuilder,
|
|
25
|
-
menuClassName,
|
|
26
|
-
menuLocation,
|
|
27
|
-
onMenuClose,
|
|
28
|
-
onMenuOpen,
|
|
29
|
-
menuOptions,
|
|
30
|
-
popupPlacement,
|
|
31
|
-
tabIndex
|
|
32
|
-
});
|
|
33
|
-
if (label) return /*#__PURE__*/ jsxs(Button, {
|
|
34
|
-
...htmlAttributes,
|
|
35
|
-
...ariaAttributes,
|
|
36
|
-
...buttonProps,
|
|
37
|
-
appearance: appearance,
|
|
38
|
-
className: clsx(classBase, className, `${classBase}-withCaption`, {
|
|
39
|
-
"saltButton-active": menuOpen
|
|
40
|
-
}),
|
|
41
|
-
disabled: disabled,
|
|
42
|
-
ref: rootRef,
|
|
43
|
-
sentiment: sentiment,
|
|
44
|
-
children: [
|
|
45
|
-
icon ? /*#__PURE__*/ jsx(Icon, {
|
|
46
|
-
name: icon
|
|
47
|
-
}) : null,
|
|
48
|
-
label
|
|
49
|
-
]
|
|
50
|
-
});
|
|
51
|
-
if (icon) return /*#__PURE__*/ jsx(IconButton, {
|
|
52
|
-
...htmlAttributes,
|
|
53
|
-
...ariaAttributes,
|
|
54
|
-
...buttonProps,
|
|
55
|
-
appearance: appearance,
|
|
56
|
-
className: clsx(classBase, className, {
|
|
57
|
-
"saltButton-active": menuOpen
|
|
58
|
-
}),
|
|
59
|
-
disabled: disabled,
|
|
60
|
-
icon: icon,
|
|
61
|
-
ref: rootRef,
|
|
62
|
-
sentiment: sentiment
|
|
63
|
-
});
|
|
64
|
-
console.error("PopupMenu must have a label or an icon (or both)");
|
|
65
|
-
return null;
|
|
66
|
-
};
|
|
67
|
-
export { PopupMenu_PopupMenu as PopupMenu };
|
package/src/popup-menu/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./PopupMenu.js";
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { useCallback, useRef, useState } from "react";
|
|
2
|
-
import { getPositionRelativeToAnchor } from "../popup/getPositionRelativeToAnchor.js";
|
|
3
|
-
import { useContextMenu } from "@vuu-ui/vuu-context-menu";
|
|
4
|
-
const usePopupMenu = ({ anchorElement, "aria-label": ariaLabel = "Popup menu", id, menuActionHandler, menuBuilder, menuLocation, menuOptions, onMenuClose, onMenuOpen, popupPlacement, tabIndex })=>{
|
|
5
|
-
const [menuOpen, _setMenuOpen] = useState(false);
|
|
6
|
-
const suppressShowMenuRef = useRef(false);
|
|
7
|
-
const rootRef = useRef(null);
|
|
8
|
-
const setMenuOpen = useCallback((isOpen)=>{
|
|
9
|
-
_setMenuOpen(isOpen);
|
|
10
|
-
if (isOpen) {
|
|
11
|
-
onMenuOpen?.();
|
|
12
|
-
setTimeout(()=>{
|
|
13
|
-
const firstOption = document.activeElement?.querySelector(".saltMenuItem");
|
|
14
|
-
firstOption?.focus();
|
|
15
|
-
}, 40);
|
|
16
|
-
} else suppressShowMenuRef.current = false;
|
|
17
|
-
}, [
|
|
18
|
-
onMenuOpen
|
|
19
|
-
]);
|
|
20
|
-
const showContextMenu = useContextMenu(menuBuilder, menuActionHandler);
|
|
21
|
-
const handleMenuOpenChange = useCallback((isOpen)=>{
|
|
22
|
-
if (!isOpen) {
|
|
23
|
-
setMenuOpen(false);
|
|
24
|
-
onMenuClose?.();
|
|
25
|
-
}
|
|
26
|
-
}, [
|
|
27
|
-
onMenuClose,
|
|
28
|
-
setMenuOpen
|
|
29
|
-
]);
|
|
30
|
-
const showMenu = useCallback((e)=>{
|
|
31
|
-
if (suppressShowMenuRef.current) suppressShowMenuRef.current = false;
|
|
32
|
-
else {
|
|
33
|
-
suppressShowMenuRef.current = true;
|
|
34
|
-
const anchorEl = anchorElement?.current ?? rootRef.current;
|
|
35
|
-
if (anchorEl) {
|
|
36
|
-
const { left: x, top: y } = getPositionRelativeToAnchor(anchorEl, popupPlacement, 0, 0);
|
|
37
|
-
setMenuOpen(true);
|
|
38
|
-
showContextMenu(e, menuLocation, menuOptions, {
|
|
39
|
-
onOpenChange: handleMenuOpenChange,
|
|
40
|
-
x,
|
|
41
|
-
y
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}, [
|
|
46
|
-
anchorElement,
|
|
47
|
-
popupPlacement,
|
|
48
|
-
setMenuOpen,
|
|
49
|
-
showContextMenu,
|
|
50
|
-
menuLocation,
|
|
51
|
-
menuOptions,
|
|
52
|
-
handleMenuOpenChange
|
|
53
|
-
]);
|
|
54
|
-
const ariaAttributes = {
|
|
55
|
-
"aria-controls": menuOpen ? `${id}-menu` : void 0,
|
|
56
|
-
"aria-label": ariaLabel,
|
|
57
|
-
"aria-expanded": menuOpen,
|
|
58
|
-
"aria-haspopup": "menu"
|
|
59
|
-
};
|
|
60
|
-
const buttonProps = {
|
|
61
|
-
id,
|
|
62
|
-
onClick: showMenu,
|
|
63
|
-
tabIndex
|
|
64
|
-
};
|
|
65
|
-
return {
|
|
66
|
-
ariaAttributes,
|
|
67
|
-
buttonProps,
|
|
68
|
-
menuOpen,
|
|
69
|
-
rootRef
|
|
70
|
-
};
|
|
71
|
-
};
|
|
72
|
-
export { usePopupMenu };
|
package/src/portal/Portal.css.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
const css = `
|
|
2
|
-
.vuuPortal {
|
|
3
|
-
z-index: 100;
|
|
4
|
-
position: absolute;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
.vuuPortal:not(.vuuPopup) {
|
|
8
|
-
background-color: var(--salt-container-primary-background);
|
|
9
|
-
border-color: var(--vuuMenuList-borderColor, var(--salt-container-primary-borderColor));
|
|
10
|
-
border-style: solid;
|
|
11
|
-
border-width: 1px;
|
|
12
|
-
border-radius: 4px;
|
|
13
|
-
padding: 4px 0;
|
|
14
|
-
box-shadow: 0 6px 12px #0000002d;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.vuuPortal:has(.vuuDropdown-popup-component.vuuList-empty) {
|
|
18
|
-
display: none;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
#vuu-portal-root {
|
|
22
|
-
z-index: 1000;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
`;
|
|
27
|
-
export default css;
|
package/src/portal/Portal.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { useThemeAttributes } from "@vuu-ui/vuu-utils";
|
|
2
|
-
import { useLayoutEffect, useRef, useState } from "react";
|
|
3
|
-
import { createPortal } from "react-dom";
|
|
4
|
-
import { useComponentCssInjection } from "@salt-ds/styles";
|
|
5
|
-
import { useWindow } from "@salt-ds/window";
|
|
6
|
-
import Portal from "./Portal.css";
|
|
7
|
-
function getContainer(container) {
|
|
8
|
-
return "function" == typeof container ? container() : container;
|
|
9
|
-
}
|
|
10
|
-
const DEFAULT_ID = [
|
|
11
|
-
"vuu-dialog-portal-root",
|
|
12
|
-
"vuu-portal-root"
|
|
13
|
-
];
|
|
14
|
-
const getFirstAvailableElementById = (id)=>{
|
|
15
|
-
if (!Array.isArray(id)) return document.getElementById(id);
|
|
16
|
-
for (const i of id){
|
|
17
|
-
const element = document.getElementById(i);
|
|
18
|
-
if (element) return element;
|
|
19
|
-
}
|
|
20
|
-
return null;
|
|
21
|
-
};
|
|
22
|
-
const Portal_Portal = ({ children, container: containerProp = document.body, id = DEFAULT_ID, onRender, open = true, themeAttributes })=>{
|
|
23
|
-
const targetWindow = useWindow();
|
|
24
|
-
useComponentCssInjection({
|
|
25
|
-
testId: "vuu-portal",
|
|
26
|
-
css: Portal,
|
|
27
|
-
window: targetWindow
|
|
28
|
-
});
|
|
29
|
-
const [mounted, setMounted] = useState(false);
|
|
30
|
-
const portalRef = useRef(null);
|
|
31
|
-
const container = getContainer(containerProp) ?? document.body;
|
|
32
|
-
const [themeClass, densityClass, dataMode] = useThemeAttributes(themeAttributes);
|
|
33
|
-
useLayoutEffect(()=>{
|
|
34
|
-
const root = getFirstAvailableElementById(id);
|
|
35
|
-
if (root) portalRef.current = root;
|
|
36
|
-
else {
|
|
37
|
-
portalRef.current = document.createElement("div");
|
|
38
|
-
portalRef.current.id = "string" == typeof id ? id : id.length > 0 ? id.at(-1) : "vuu-portal-root";
|
|
39
|
-
}
|
|
40
|
-
const el = portalRef.current;
|
|
41
|
-
if (!container.contains(el)) container.appendChild(el);
|
|
42
|
-
el.classList.add(themeClass, densityClass);
|
|
43
|
-
el.dataset.mode = dataMode;
|
|
44
|
-
setMounted(true);
|
|
45
|
-
}, [
|
|
46
|
-
id,
|
|
47
|
-
container,
|
|
48
|
-
themeClass,
|
|
49
|
-
densityClass,
|
|
50
|
-
dataMode
|
|
51
|
-
]);
|
|
52
|
-
useLayoutEffect(()=>{
|
|
53
|
-
requestAnimationFrame(()=>{
|
|
54
|
-
onRender?.();
|
|
55
|
-
});
|
|
56
|
-
}, [
|
|
57
|
-
onRender
|
|
58
|
-
]);
|
|
59
|
-
if (open && mounted && portalRef.current && children) return /*#__PURE__*/ createPortal(children, portalRef.current);
|
|
60
|
-
return null;
|
|
61
|
-
};
|
|
62
|
-
export { Portal_Portal as Portal };
|
package/src/portal/index.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./Portal.js";
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
const css = `
|
|
2
|
-
.vuuTooltip {
|
|
3
|
-
--tooltip-align: flex-start;
|
|
4
|
-
--tooltip-background: var(--salt-actionable-bold-background-hover);
|
|
5
|
-
--tooltip-border: var(--salt-actionable-bold-background-hover);
|
|
6
|
-
--tooltip-color: var(--salt-actionable-bold-foreground-hover);
|
|
7
|
-
--tooltip-justify: flex-start;
|
|
8
|
-
--tooltip-top: auto;
|
|
9
|
-
--tooltip-right: auto;
|
|
10
|
-
--tooltip-bottom: auto;
|
|
11
|
-
--tooltip-left: auto;
|
|
12
|
-
align-items: var(--tooltip-align);
|
|
13
|
-
justify-content: var(--tooltip-justify);
|
|
14
|
-
z-index: 100;
|
|
15
|
-
transition: opacity .15s ease-in;
|
|
16
|
-
display: flex;
|
|
17
|
-
position: absolute;
|
|
18
|
-
|
|
19
|
-
&.vuuHidden {
|
|
20
|
-
opacity: 0;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
.vuuTooltip-content {
|
|
25
|
-
background-color: var(--tooltip-background);
|
|
26
|
-
border-color: var(--tooltip-border);
|
|
27
|
-
border-radius: var(--vuuTooltip-borderRadius, 6px);
|
|
28
|
-
color: var(--tooltip-color);
|
|
29
|
-
white-space: nowrap;
|
|
30
|
-
border-style: solid;
|
|
31
|
-
border-width: 1px;
|
|
32
|
-
padding: 2px 8px;
|
|
33
|
-
line-height: 24px;
|
|
34
|
-
box-shadow: 0 4px 4px #00000040;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.vuuTooltip:before {
|
|
38
|
-
background-color: var(--tooltip-border);
|
|
39
|
-
content: " ";
|
|
40
|
-
z-index: -1;
|
|
41
|
-
width: 12px;
|
|
42
|
-
height: 12px;
|
|
43
|
-
display: block;
|
|
44
|
-
position: absolute;
|
|
45
|
-
transform: rotate(45deg);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
.vuuTooltip[data-align="above"]:before {
|
|
49
|
-
bottom: -6px;
|
|
50
|
-
left: calc(50% - 6px);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.vuuTooltip[data-align="below"]:before {
|
|
54
|
-
top: -6px;
|
|
55
|
-
left: calc(50% - 6px);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.vuuTooltip[data-align="right"]:before {
|
|
59
|
-
top: calc(50% - 6px);
|
|
60
|
-
left: -6px;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.vuuTooltip[data-align="left"]:before {
|
|
64
|
-
top: calc(50% - 6px);
|
|
65
|
-
right: -6px;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.vuuTooltip-error {
|
|
69
|
-
--tooltip-background: var(--vuu-color-red-50);
|
|
70
|
-
--tooltip-color: white;
|
|
71
|
-
color: #fff;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
`;
|
|
76
|
-
export default css;
|
package/src/tooltip/Tooltip.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Portal } from "../portal/index.js";
|
|
3
|
-
import { useTooltipAnchoredPosition } from "./useTooltipAnchoredPosition.js";
|
|
4
|
-
import clsx from "clsx";
|
|
5
|
-
import { useComponentCssInjection } from "@salt-ds/styles";
|
|
6
|
-
import { useWindow } from "@salt-ds/window";
|
|
7
|
-
import Tooltip from "./Tooltip.css";
|
|
8
|
-
const classBase = "vuuTooltip";
|
|
9
|
-
const Tooltip_Tooltip = ({ anchorElement, children, className, id, onMouseEnter, onMouseLeave, placement: placementProp, status, style: styleProp })=>{
|
|
10
|
-
const targetWindow = useWindow();
|
|
11
|
-
useComponentCssInjection({
|
|
12
|
-
testId: "vuu-tooltip",
|
|
13
|
-
css: Tooltip,
|
|
14
|
-
window: targetWindow
|
|
15
|
-
});
|
|
16
|
-
const ref = useTooltipAnchoredPosition({
|
|
17
|
-
anchorElement,
|
|
18
|
-
placement: placementProp
|
|
19
|
-
});
|
|
20
|
-
return /*#__PURE__*/ jsx(Portal, {
|
|
21
|
-
children: /*#__PURE__*/ jsx("div", {
|
|
22
|
-
className: clsx(classBase, className, "vuuHidden", {
|
|
23
|
-
[`${classBase}-error`]: "error" === status
|
|
24
|
-
}),
|
|
25
|
-
id: id,
|
|
26
|
-
ref: ref,
|
|
27
|
-
style: {
|
|
28
|
-
...styleProp,
|
|
29
|
-
left: 0,
|
|
30
|
-
top: 0
|
|
31
|
-
},
|
|
32
|
-
children: /*#__PURE__*/ jsx("span", {
|
|
33
|
-
className: `${classBase}-content`,
|
|
34
|
-
onMouseEnter: onMouseEnter,
|
|
35
|
-
onMouseLeave: onMouseLeave,
|
|
36
|
-
children: children
|
|
37
|
-
})
|
|
38
|
-
})
|
|
39
|
-
});
|
|
40
|
-
};
|
|
41
|
-
export { Tooltip_Tooltip as Tooltip };
|
package/src/tooltip/index.js
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { queryClosest, useId } from "@vuu-ui/vuu-utils";
|
|
2
|
-
import { useCallback, useRef, useState } from "react";
|
|
3
|
-
const useTooltip = ({ anchorQuery = "*", id: idProp, placement = [
|
|
4
|
-
"right",
|
|
5
|
-
"above",
|
|
6
|
-
"below"
|
|
7
|
-
], tooltipContent })=>{
|
|
8
|
-
const hideTooltipRef = useRef(void 0);
|
|
9
|
-
const isHoveringRef = useRef(false);
|
|
10
|
-
const anchorElementRef = useRef(null);
|
|
11
|
-
const mouseEnterTimerRef = useRef(void 0);
|
|
12
|
-
const mouseLeaveTimerRef = useRef(void 0);
|
|
13
|
-
const [tooltipProps, setTooltipProps] = useState();
|
|
14
|
-
const id = useId(idProp);
|
|
15
|
-
const escapeListener = useCallback((evt)=>{
|
|
16
|
-
if ("Escape" === evt.key) hideTooltipRef.current?.();
|
|
17
|
-
}, []);
|
|
18
|
-
hideTooltipRef.current = useCallback(()=>{
|
|
19
|
-
setTooltipProps(void 0);
|
|
20
|
-
document.removeEventListener("keydown", escapeListener);
|
|
21
|
-
}, [
|
|
22
|
-
escapeListener
|
|
23
|
-
]);
|
|
24
|
-
const handleMouseEnterTooltip = useCallback(()=>{
|
|
25
|
-
window.clearTimeout(mouseLeaveTimerRef.current);
|
|
26
|
-
}, []);
|
|
27
|
-
const handleMouseLeaveTooltip = useCallback(()=>{
|
|
28
|
-
hideTooltipRef.current?.();
|
|
29
|
-
}, []);
|
|
30
|
-
const hideTooltip = useCallback((defer = 0)=>{
|
|
31
|
-
if (mouseEnterTimerRef.current) {
|
|
32
|
-
window.clearTimeout(mouseEnterTimerRef.current);
|
|
33
|
-
mouseEnterTimerRef.current = void 0;
|
|
34
|
-
} else if (hideTooltipRef.current) if (0 === defer) hideTooltipRef.current();
|
|
35
|
-
else mouseLeaveTimerRef.current = window.setTimeout(hideTooltipRef.current, defer);
|
|
36
|
-
}, []);
|
|
37
|
-
const showTooltip = useCallback((ref = anchorElementRef)=>{
|
|
38
|
-
const { current: anchorEl } = ref;
|
|
39
|
-
if (anchorEl) {
|
|
40
|
-
setTooltipProps({
|
|
41
|
-
anchorElement: ref,
|
|
42
|
-
children: tooltipContent,
|
|
43
|
-
id: `${id}-tooltip`,
|
|
44
|
-
onMouseEnter: handleMouseEnterTooltip,
|
|
45
|
-
onMouseLeave: handleMouseLeaveTooltip,
|
|
46
|
-
placement: placement
|
|
47
|
-
});
|
|
48
|
-
document.addEventListener("keydown", escapeListener);
|
|
49
|
-
}
|
|
50
|
-
mouseEnterTimerRef.current = void 0;
|
|
51
|
-
hideTooltip(isHoveringRef.current ? 3000 : 1000);
|
|
52
|
-
}, [
|
|
53
|
-
escapeListener,
|
|
54
|
-
handleMouseEnterTooltip,
|
|
55
|
-
handleMouseLeaveTooltip,
|
|
56
|
-
hideTooltip,
|
|
57
|
-
id,
|
|
58
|
-
placement,
|
|
59
|
-
tooltipContent
|
|
60
|
-
]);
|
|
61
|
-
const handleMouseEnter = useCallback((evt)=>{
|
|
62
|
-
isHoveringRef.current = true;
|
|
63
|
-
const el = queryClosest(evt.target, anchorQuery);
|
|
64
|
-
if (el) {
|
|
65
|
-
console.log(`el ${el.classList}`);
|
|
66
|
-
anchorElementRef.current = el;
|
|
67
|
-
mouseEnterTimerRef.current = window.setTimeout(showTooltip, 800);
|
|
68
|
-
}
|
|
69
|
-
}, [
|
|
70
|
-
anchorQuery,
|
|
71
|
-
showTooltip
|
|
72
|
-
]);
|
|
73
|
-
const handleMouseLeave = useCallback(()=>{
|
|
74
|
-
isHoveringRef.current = false;
|
|
75
|
-
hideTooltip(200);
|
|
76
|
-
}, [
|
|
77
|
-
hideTooltip
|
|
78
|
-
]);
|
|
79
|
-
const anchorProps = {
|
|
80
|
-
"aria-describedby": `${id}-tooltip`,
|
|
81
|
-
onMouseEnter: handleMouseEnter,
|
|
82
|
-
onMouseLeave: handleMouseLeave
|
|
83
|
-
};
|
|
84
|
-
return {
|
|
85
|
-
anchorProps,
|
|
86
|
-
hideTooltip,
|
|
87
|
-
showTooltip,
|
|
88
|
-
tooltipProps
|
|
89
|
-
};
|
|
90
|
-
};
|
|
91
|
-
export { useTooltip };
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { useCallback } from "react";
|
|
2
|
-
const pointerSize = 12;
|
|
3
|
-
const roomAbove = (anchor, tooltip)=>tooltip.height + pointerSize < anchor.top;
|
|
4
|
-
const roomLeft = (anchor, tooltip)=>tooltip.width + pointerSize < anchor.left;
|
|
5
|
-
const roomRight = (anchor, tooltip)=>anchor.right + tooltip.width + pointerSize < document.body.clientWidth;
|
|
6
|
-
const roomBelow = (anchor, tooltip)=>document.body.clientHeight - anchor.bottom > tooltip.height + pointerSize;
|
|
7
|
-
const roomAvailableAtPlacement = (placement, anchor, tooltip)=>{
|
|
8
|
-
switch(placement){
|
|
9
|
-
case "above":
|
|
10
|
-
return roomAbove(anchor, tooltip);
|
|
11
|
-
case "left":
|
|
12
|
-
return roomLeft(anchor, tooltip);
|
|
13
|
-
case "below":
|
|
14
|
-
return roomBelow(anchor, tooltip);
|
|
15
|
-
case "right":
|
|
16
|
-
return roomRight(anchor, tooltip);
|
|
17
|
-
default:
|
|
18
|
-
throw Error("invalid tooltip placement");
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
const positionAbove = (anchor, tooltip)=>({
|
|
22
|
-
left: anchor.left - (tooltip.width - anchor.width) / 2,
|
|
23
|
-
top: anchor.top - (tooltip.height + pointerSize)
|
|
24
|
-
});
|
|
25
|
-
const positionBelow = (anchor, tooltip)=>({
|
|
26
|
-
left: anchor.left - (tooltip.width - anchor.width) / 2,
|
|
27
|
-
top: anchor.bottom + pointerSize
|
|
28
|
-
});
|
|
29
|
-
const positionLeft = (anchor, tooltip)=>({
|
|
30
|
-
left: anchor.left - pointerSize - tooltip.width,
|
|
31
|
-
top: anchor.top - (tooltip.height - anchor.height) / 2
|
|
32
|
-
});
|
|
33
|
-
const positionRight = (anchor, tooltip)=>({
|
|
34
|
-
left: anchor.right + pointerSize,
|
|
35
|
-
top: anchor.top - (tooltip.height - anchor.height) / 2
|
|
36
|
-
});
|
|
37
|
-
const positionAtPlacement = (placement, anchor, tooltip)=>{
|
|
38
|
-
switch(placement){
|
|
39
|
-
case "above":
|
|
40
|
-
return positionAbove(anchor, tooltip);
|
|
41
|
-
case "left":
|
|
42
|
-
return positionLeft(anchor, tooltip);
|
|
43
|
-
case "below":
|
|
44
|
-
return positionBelow(anchor, tooltip);
|
|
45
|
-
case "right":
|
|
46
|
-
return positionRight(anchor, tooltip);
|
|
47
|
-
default:
|
|
48
|
-
throw Error("invalid tooltip placement");
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
|
-
const keepWithinTheScreen = ({ height, width }, position)=>{
|
|
52
|
-
const { clientWidth, clientHeight } = document.body;
|
|
53
|
-
let { left, top } = position;
|
|
54
|
-
if (left + width > clientWidth) left -= left + width - clientWidth;
|
|
55
|
-
if (left < 0) left = 0;
|
|
56
|
-
if (top + height > clientHeight) top -= top + height - clientHeight;
|
|
57
|
-
if (top < 0) top = 0;
|
|
58
|
-
return {
|
|
59
|
-
left,
|
|
60
|
-
top
|
|
61
|
-
};
|
|
62
|
-
};
|
|
63
|
-
const toCSSText = ({ left, top })=>`left:${left}px;top:${top}px;opacity:1;`;
|
|
64
|
-
const getNextPlacement = (placement)=>{
|
|
65
|
-
if (!Array.isArray(placement)) return [
|
|
66
|
-
placement,
|
|
67
|
-
[]
|
|
68
|
-
];
|
|
69
|
-
if (0 === placement.length) return [
|
|
70
|
-
void 0,
|
|
71
|
-
placement
|
|
72
|
-
];
|
|
73
|
-
return [
|
|
74
|
-
placement[0],
|
|
75
|
-
placement.slice(1)
|
|
76
|
-
];
|
|
77
|
-
};
|
|
78
|
-
const useTooltipAnchoredPosition = ({ anchorElement, placement })=>{
|
|
79
|
-
const ref = useCallback((el)=>{
|
|
80
|
-
if (el && anchorElement.current) {
|
|
81
|
-
const anchorRect = anchorElement.current.getBoundingClientRect();
|
|
82
|
-
const tooltipRect = el.getBoundingClientRect();
|
|
83
|
-
let nextPlacement;
|
|
84
|
-
let placements = placement;
|
|
85
|
-
[nextPlacement = "right", placements] = getNextPlacement(placements);
|
|
86
|
-
do {
|
|
87
|
-
if (roomAvailableAtPlacement(nextPlacement, anchorRect, tooltipRect)) {
|
|
88
|
-
el.style.cssText = toCSSText(keepWithinTheScreen(tooltipRect, positionAtPlacement(nextPlacement, anchorRect, tooltipRect)));
|
|
89
|
-
el.dataset.align = nextPlacement;
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
[nextPlacement, placements] = getNextPlacement(placements);
|
|
93
|
-
}while (nextPlacement)
|
|
94
|
-
}
|
|
95
|
-
el?.classList.remove("vuuHidden");
|
|
96
|
-
}, [
|
|
97
|
-
anchorElement,
|
|
98
|
-
placement
|
|
99
|
-
]);
|
|
100
|
-
return ref;
|
|
101
|
-
};
|
|
102
|
-
export { useTooltipAnchoredPosition };
|