UNPKG

@grafana/ui

Version:
309 lines (302 loc) • 10.2 kB
'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