@grafana/ui
Version:
Grafana Components Library
309 lines (302 loc) • 10.2 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var jsxRuntime = require('react/jsx-runtime');
var css = require('@emotion/css');
var react = require('@floating-ui/react');
var RcDrawer = require('@rc-component/drawer');
var React = require('react');
var e2eSelectors = require('@grafana/e2e-selectors');
var i18n = require('@grafana/i18n');
var ThemeContext = require('../../themes/ThemeContext.cjs');
var DragHandle = require('../DragHandle/DragHandle.cjs');
var IconButton = require('../IconButton/IconButton.cjs');
var Stack = require('../Layout/Stack/Stack.cjs');
var Portal = require('../Portal/Portal.cjs');
var ScrollContainer = require('../ScrollContainer/ScrollContainer.cjs');
var Text = require('../Text/Text.cjs');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
var RcDrawer__default = /*#__PURE__*/_interopDefaultCompat(RcDrawer);
"use strict";
const drawerSizes = {
sm: { width: "25vw", minWidth: 384 },
md: { width: "50vw", minWidth: 568 },
lg: { width: "75vw", minWidth: 744 }
};
function Drawer({
children,
onClose,
closeOnMaskClick = true,
scrollableContent = true,
title,
subtitle,
width,
size = "md",
tabs
}) {
var _a;
const [drawerWidth, onMouseDown, onTouchStart] = useResizebleDrawer();
const styles = ThemeContext.useStyles2(getStyles);
const wrapperStyles = ThemeContext.useStyles2(getWrapperStyles, size);
const dragStyles = ThemeContext.useStyles2(DragHandle.getDragStyles);
const titleId = React.useId();
const { context, refs } = react.useFloating({
open: true,
onOpenChange: (open) => {
if (!open) {
onClose == null ? void 0 : onClose();
}
}
});
useBodyClassWhileOpen();
const content = /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.content, children });
const overrideWidth = (_a = drawerWidth != null ? drawerWidth : width) != null ? _a : drawerSizes[size].width;
const minWidth = drawerSizes[size].minWidth;
return /* @__PURE__ */ jsxRuntime.jsx(
RcDrawer__default.default,
{
open: true,
onClose,
placement: "right",
getContainer: ".main-view",
className: styles.drawerContent,
rootClassName: styles.drawer,
classNames: {
wrapper: wrapperStyles
},
styles: {
wrapper: {
width: overrideWidth,
minWidth
}
},
"aria-label": typeof title === "string" ? e2eSelectors.selectors.components.Drawer.General.title(title) : void 0,
"aria-labelledby": title ? titleId : void 0,
width: "",
motion: {
motionAppear: true,
motionName: styles.drawerMotion
},
maskClassName: styles.mask,
maskClosable: closeOnMaskClick,
maskMotion: {
motionAppear: true,
motionName: styles.maskMotion
},
autoFocus: false,
children: /* @__PURE__ */ jsxRuntime.jsx(react.FloatingFocusManager, { context, modal: true, getInsideElements: () => [Portal.getPortalContainer()], children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.container, ref: refs.setFloating, children: [
/* @__PURE__ */ jsxRuntime.jsx(
"div",
{
className: css.cx(dragStyles.dragHandleVertical, styles.resizer),
onMouseDown,
onTouchStart
}
),
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: css.cx(styles.header, Boolean(tabs) && styles.headerWithTabs), children: [
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.actions, children: /* @__PURE__ */ jsxRuntime.jsx(
IconButton.IconButton,
{
name: "times",
variant: "secondary",
onClick: onClose,
"data-testid": e2eSelectors.selectors.components.Drawer.General.close,
tooltip: i18n.t(`grafana-ui.drawer.close`, "Close")
}
) }),
typeof title === "string" ? /* @__PURE__ */ jsxRuntime.jsxs(Stack.Stack, { direction: "column", children: [
/* @__PURE__ */ jsxRuntime.jsx(Text.Text, { element: "h3", id: titleId, truncate: true, children: title }),
subtitle && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.subtitle, "data-testid": e2eSelectors.selectors.components.Drawer.General.subtitle, children: subtitle })
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { id: titleId, children: title }),
tabs && /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.tabsWrapper, children: tabs })
] }),
!scrollableContent ? content : /* @__PURE__ */ jsxRuntime.jsx(ScrollContainer.ScrollContainer, { showScrollIndicators: true, children: content })
] }) })
}
);
}
function useResizebleDrawer() {
const [drawerWidth, setDrawerWidth] = React.useState(void 0);
const onMouseMove = React.useCallback((e) => {
setDrawerWidth(getCustomDrawerWidth(e.clientX));
}, []);
const onTouchMove = React.useCallback((e) => {
const touch = e.touches[0];
setDrawerWidth(getCustomDrawerWidth(touch.clientX));
}, []);
const onMouseUp = React.useCallback(
(e) => {
document.removeEventListener("mousemove", onMouseMove);
document.removeEventListener("mouseup", onMouseUp);
},
[onMouseMove]
);
const onTouchEnd = React.useCallback(
(e) => {
document.removeEventListener("touchmove", onTouchMove);
document.removeEventListener("touchend", onTouchEnd);
},
[onTouchMove]
);
function onMouseDown(e) {
e.stopPropagation();
e.preventDefault();
document.addEventListener("mousemove", onMouseMove);
document.addEventListener("mouseup", onMouseUp);
}
function onTouchStart(e) {
e.stopPropagation();
e.preventDefault();
document.addEventListener("touchmove", onTouchMove);
document.addEventListener("touchend", onTouchEnd);
}
return [drawerWidth, onMouseDown, onTouchStart];
}
function getCustomDrawerWidth(clientX) {
let offsetRight = document.body.offsetWidth - (clientX - document.body.offsetLeft);
let widthPercent = Math.min(offsetRight / document.body.clientWidth * 100, 98).toFixed(2);
return `${widthPercent}vw`;
}
function useBodyClassWhileOpen() {
React.useEffect(() => {
if (!document.body) {
return;
}
document.body.classList.add("body-drawer-open");
return () => {
document.body.classList.remove("body-drawer-open");
};
}, []);
}
const getStyles = (theme) => {
var _a, _b;
return {
container: css.css({
display: "flex",
flexDirection: "column",
height: "100%",
flex: "1 1 0",
minHeight: "100%",
position: "relative"
}),
drawer: css.css({
inset: 0,
position: "fixed",
zIndex: theme.zIndex.modalBackdrop,
pointerEvents: "none",
".rc-drawer-content-wrapper": {
boxShadow: theme.shadows.z3
}
}),
drawerContent: css.css({
backgroundColor: theme.colors.background.primary,
display: "flex",
flexDirection: "column",
height: "100%",
pointerEvents: "auto",
width: "100%"
}),
drawerMotion: css.css({
"&-appear": {
[theme.transitions.handleMotion("no-preference")]: {
transform: "translateX(100%)",
transition: "none"
},
[theme.transitions.handleMotion("reduce")]: {
opacity: 0
},
"&-active": {
[theme.transitions.handleMotion("no-preference")]: {
transform: "translateX(0)",
transition: theme.transitions.create("transform")
},
[theme.transitions.handleMotion("reduce")]: {
transition: `opacity 0.2s ease-in-out`,
opacity: 1
}
}
}
}),
// we want the mask itself to span the whole page including the top bar
// this ensures trying to click something in the top bar will close the drawer correctly
// but we don't want the backdrop styling to apply over the top bar as it looks weird
// instead have a child pseudo element to apply the backdrop styling below the top bar
mask: css.css({
inset: 0,
pointerEvents: "auto",
position: "fixed",
zIndex: theme.zIndex.modalBackdrop,
"&:before": {
backgroundColor: theme.components.overlay.background,
bottom: 0,
content: '""',
left: 0,
position: "fixed",
right: 0,
top: 0
}
}),
maskMotion: css.css({
"&-appear": {
opacity: 0,
"&-active": {
opacity: 1,
[theme.transitions.handleMotion("no-preference", "reduce")]: {
transition: theme.transitions.create("opacity")
}
}
}
}),
header: css.css({
label: "drawer-header",
flexGrow: 0,
padding: theme.spacing(2, 2, 3),
borderBottom: `1px solid ${theme.colors.border.weak}`
}),
headerWithTabs: css.css({
borderBottom: "none"
}),
actions: css.css({
position: "absolute",
right: theme.spacing(1),
top: theme.spacing(1)
}),
subtitle: css.css({
label: "drawer-subtitle",
color: theme.colors.text.secondary
}),
content: css.css({
padding: theme.spacing((_b = (_a = theme.components.drawer) == null ? void 0 : _a.padding) != null ? _b : 2),
height: "100%",
flexGrow: 1,
minHeight: 0
}),
tabsWrapper: css.css({
label: "drawer-tabs",
paddingLeft: theme.spacing(2),
margin: theme.spacing(1, -1, -3, -3)
}),
resizer: css.css({
top: 0,
left: theme.spacing(-1),
bottom: 0,
position: "absolute",
zIndex: theme.zIndex.modal
})
};
};
function getWrapperStyles(theme, size) {
return css.css({
bottom: 0,
label: `drawer-content-wrapper-${size}`,
position: "absolute",
right: 0,
top: 0,
zIndex: theme.zIndex.modalBackdrop,
[theme.breakpoints.down("md")]: {
width: `calc(100% - ${theme.spacing(2)}) !important`,
minWidth: "0 !important"
}
});
}
exports.Drawer = Drawer;
//# sourceMappingURL=Drawer.cjs.map