UNPKG

@grafana/ui

Version:
1,673 lines (1,644 loc) • 328 kB
'use strict'; var jsxRuntime = require('react/jsx-runtime'); var css = require('@emotion/css'); require('react-data-grid/lib/styles.css'); var React = require('react'); var reactDataGrid = require('react-data-grid'); var data = require('@grafana/data'); var i18n = require('@grafana/i18n'); var schema = require('@grafana/schema'); var hoistNonReactStatics = require('hoist-non-react-statics'); var memoize = require('micro-memoize'); var reactUse = require('react-use'); var e2eSelectors = require('@grafana/e2e-selectors'); var lodash = require('lodash'); var tinycolor = require('tinycolor2'); var SVG = require('react-inlinesvg'); var ReactDOM = require('react-dom'); var react = require('@floating-ui/react'); var ReactDOMServer = require('react-dom/server'); var reactTransitionGroup = require('react-transition-group'); var dialog = require('@react-aria/dialog'); var focus = require('@react-aria/focus'); var overlays = require('@react-aria/overlays'); var reactHookForm = require('react-hook-form'); var RcDrawer = require('rc-drawer'); require('rc-drawer/assets/index.css'); var reactWindow = require('react-window'); var format = require('ol/format'); var geom = require('ol/geom'); var uwrap = require('uwrap'); var WKT = require('ol/format/WKT'); var uPlot = require('uplot'); require('uplot/dist/uPlot.min.css'); function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; } function _interopNamespaceCompat(e) { if (e && typeof e === 'object' && 'default' in e) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var React__namespace = /*#__PURE__*/_interopNamespaceCompat(React); var hoistNonReactStatics__default = /*#__PURE__*/_interopDefaultCompat(hoistNonReactStatics); var memoize__default = /*#__PURE__*/_interopDefaultCompat(memoize); var tinycolor__default = /*#__PURE__*/_interopDefaultCompat(tinycolor); var SVG__default = /*#__PURE__*/_interopDefaultCompat(SVG); var ReactDOM__default = /*#__PURE__*/_interopDefaultCompat(ReactDOM); var ReactDOMServer__default = /*#__PURE__*/_interopDefaultCompat(ReactDOMServer); var RcDrawer__default = /*#__PURE__*/_interopDefaultCompat(RcDrawer); var WKT__default = /*#__PURE__*/_interopDefaultCompat(WKT); var uPlot__default = /*#__PURE__*/_interopDefaultCompat(uPlot); const fadeIn = css.keyframes({ "0%": { opacity: 0 }, "100%": { opacity: 1 } }); const skeletonAnimation = { animationName: fadeIn, animationDelay: "100ms", animationTimingFunction: "ease-in", animationDuration: "100ms", animationFillMode: "backwards" }; const attachSkeleton = (Component, Skeleton) => { const skeletonWrapper = (props) => { return /* @__PURE__ */ jsxRuntime.jsx( Skeleton, { ...props, rootProps: { style: skeletonAnimation } } ); }; return Object.assign(Component, { Skeleton: skeletonWrapper }); }; function stylesFactory(stylesCreator) { return memoize__default.default(stylesCreator); } const memoizedStyleCreators = /* @__PURE__ */ new WeakMap(); const withTheme2 = (Component) => { const WithTheme = (props) => { const ContextComponent = data.ThemeContext; return ( // @ts-ignore /* @__PURE__ */ jsxRuntime.jsx(ContextComponent.Consumer, { children: (theme) => /* @__PURE__ */ jsxRuntime.jsx(Component, { ...props, theme }) }) ); }; WithTheme.displayName = `WithTheme(${Component.displayName})`; hoistNonReactStatics__default.default(WithTheme, Component); return WithTheme; }; function useTheme2() { return React.useContext(data.ThemeContext); } function useStyles2(getStyles, ...additionalArguments) { const theme = useTheme2(); if (!theme.colors.background.elevated) { theme.colors.background.elevated = theme.colors.mode === "light" ? theme.colors.background.primary : theme.colors.background.secondary; } let memoizedStyleCreator = memoizedStyleCreators.get(getStyles); if (!memoizedStyleCreator) { memoizedStyleCreator = memoize__default.default(getStyles, { maxSize: 10 }); memoizedStyleCreators.set(getStyles, memoizedStyleCreator); } return memoizedStyleCreator(theme, ...additionalArguments); } function breakpointCSS(theme, prop, getCSS, key) { const value = prop[key]; if (value !== void 0 && value !== null) { return { [theme.breakpoints.up(key)]: getCSS(value) }; } return; } function getResponsiveStyle(theme, prop, getCSS) { if (prop === void 0 || prop === null) { return null; } if (typeof prop !== "object" || !("xs" in prop)) { return getCSS(prop); } return [ breakpointCSS(theme, prop, getCSS, "xs"), breakpointCSS(theme, prop, getCSS, "sm"), breakpointCSS(theme, prop, getCSS, "md"), breakpointCSS(theme, prop, getCSS, "lg"), breakpointCSS(theme, prop, getCSS, "xl"), breakpointCSS(theme, prop, getCSS, "xxl") ]; } const getSizeStyles = (theme, width, minWidth, maxWidth, height, minHeight, maxHeight) => { return css.css([ getResponsiveStyle(theme, width, (val) => ({ width: theme.spacing(val) })), getResponsiveStyle(theme, minWidth, (val) => ({ minWidth: theme.spacing(val) })), getResponsiveStyle(theme, maxWidth, (val) => ({ maxWidth: theme.spacing(val) })), getResponsiveStyle(theme, height, (val) => ({ height: theme.spacing(val) })), getResponsiveStyle(theme, minHeight, (val) => ({ minHeight: theme.spacing(val) })), getResponsiveStyle(theme, maxHeight, (val) => ({ maxHeight: theme.spacing(val) })) ]); }; const Box = React.forwardRef((props, ref) => { const { children, margin, marginX, marginY, marginTop, marginBottom, marginLeft, marginRight, padding, paddingX, paddingY, paddingTop, paddingBottom, paddingLeft, paddingRight, display, backgroundColor, grow, shrink, basis, flex, borderColor, borderStyle, borderRadius, direction, justifyContent, alignItems, boxShadow, element, gap, width, minWidth, maxWidth, height, minHeight, maxHeight, position, ...rest } = props; const styles = useStyles2( getStyles$K, margin, marginX, marginY, marginTop, marginBottom, marginLeft, marginRight, padding, paddingX, paddingY, paddingTop, paddingBottom, paddingLeft, paddingRight, display, backgroundColor, grow, shrink, basis, flex, borderColor, borderStyle, borderRadius, direction, justifyContent, alignItems, boxShadow, gap, position ); const sizeStyles = useStyles2(getSizeStyles, width, minWidth, maxWidth, height, minHeight, maxHeight); const Element = element != null ? element : "div"; return /* @__PURE__ */ jsxRuntime.jsx(Element, { ref, className: css.cx(styles.root, sizeStyles), ...rest, children }); }); Box.displayName = "Box"; const customBorderColor = (color, theme) => { switch (color) { case "error": case "success": case "info": case "warning": return theme.colors[color].borderTransparent; default: return color ? theme.colors.border[color] : void 0; } }; const customBackgroundColor = (color, theme) => { switch (color) { case "error": case "success": case "info": case "warning": return theme.colors[color].transparent; default: return color ? theme.colors.background[color] : void 0; } }; const getStyles$K = (theme, margin, marginX, marginY, marginTop, marginBottom, marginLeft, marginRight, padding, paddingX, paddingY, paddingTop, paddingBottom, paddingLeft, paddingRight, display, backgroundColor, grow, shrink, basis, flex, borderColor, borderStyle, borderRadius, direction, justifyContent, alignItems, boxShadow, gap, position) => { return { root: css.css([ getResponsiveStyle(theme, margin, (val) => ({ margin: theme.spacing(val) })), getResponsiveStyle(theme, marginX, (val) => ({ marginLeft: theme.spacing(val), marginRight: theme.spacing(val) })), getResponsiveStyle(theme, marginY, (val) => ({ marginTop: theme.spacing(val), marginBottom: theme.spacing(val) })), getResponsiveStyle(theme, marginTop, (val) => ({ marginTop: theme.spacing(val) })), getResponsiveStyle(theme, marginBottom, (val) => ({ marginBottom: theme.spacing(val) })), getResponsiveStyle(theme, marginLeft, (val) => ({ marginLeft: theme.spacing(val) })), getResponsiveStyle(theme, marginRight, (val) => ({ marginRight: theme.spacing(val) })), getResponsiveStyle(theme, padding, (val) => ({ padding: theme.spacing(val) })), getResponsiveStyle(theme, paddingX, (val) => ({ paddingLeft: theme.spacing(val), paddingRight: theme.spacing(val) })), getResponsiveStyle(theme, paddingY, (val) => ({ paddingTop: theme.spacing(val), paddingBottom: theme.spacing(val) })), getResponsiveStyle(theme, paddingTop, (val) => ({ paddingTop: theme.spacing(val) })), getResponsiveStyle(theme, paddingBottom, (val) => ({ paddingBottom: theme.spacing(val) })), getResponsiveStyle(theme, paddingLeft, (val) => ({ paddingLeft: theme.spacing(val) })), getResponsiveStyle(theme, paddingRight, (val) => ({ paddingRight: theme.spacing(val) })), getResponsiveStyle(theme, display, (val) => ({ display: val })), getResponsiveStyle(theme, backgroundColor, (val) => ({ backgroundColor: customBackgroundColor(val, theme) })), getResponsiveStyle(theme, direction, (val) => ({ flexDirection: val })), getResponsiveStyle(theme, grow, (val) => ({ flexGrow: val })), getResponsiveStyle(theme, shrink, (val) => ({ flexShrink: val })), getResponsiveStyle(theme, basis, (val) => ({ flexBasis: val })), getResponsiveStyle(theme, flex, (val) => ({ flex: val })), getResponsiveStyle(theme, borderStyle, (val) => ({ borderStyle: val })), getResponsiveStyle(theme, borderColor, (val) => ({ borderColor: customBorderColor(val, theme) })), (borderStyle || borderColor) && { borderWidth: "1px" }, getResponsiveStyle(theme, justifyContent, (val) => ({ justifyContent: val })), getResponsiveStyle(theme, alignItems, (val) => ({ alignItems: val })), getResponsiveStyle(theme, borderRadius, (val) => ({ borderRadius: theme.shape.radius[val] })), getResponsiveStyle(theme, boxShadow, (val) => ({ boxShadow: theme.shadows[val] })), getResponsiveStyle(theme, gap, (val) => ({ gap: theme.spacing(val) })), getResponsiveStyle(theme, position, (val) => ({ position: val })) ]) }; }; function MenuDivider() { const styles = useStyles2(getStyles$J); return /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.divider }); } const getStyles$J = (theme) => { return { divider: css.css({ height: 1, backgroundColor: theme.colors.border.weak, margin: theme.spacing(0.5, 0) }) }; }; const MenuGroup = ({ label, ariaLabel, children }) => { const styles = useStyles2(getStyles$I); const labelID = `group-label-${lodash.uniqueId()}`; return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-labelledby": !ariaLabel && label ? labelID : void 0, "aria-label": ariaLabel, children: [ label && /* @__PURE__ */ jsxRuntime.jsx("label", { id: labelID, className: styles.groupLabel, "aria-hidden": true, children: label }), children ] }); }; MenuGroup.displayName = "MenuGroup"; const getStyles$I = (theme) => { return { groupLabel: css.css({ color: theme.colors.text.secondary, fontSize: theme.typography.size.sm, padding: theme.spacing(0.5, 1) }) }; }; function mediaUp(breakpoint) { return `only screen and (min-width: ${breakpoint})`; } function getMouseFocusStyles(theme) { return { outline: "none", boxShadow: `none` }; } function getFocusStyles(theme) { return { outline: "2px dotted transparent", outlineOffset: "2px", boxShadow: `0 0 0 2px ${theme.colors.background.canvas}, 0 0 0px 4px ${theme.colors.primary.main}`, transitionTimingFunction: `cubic-bezier(0.19, 1, 0.22, 1)`, transitionDuration: "0.2s", transitionProperty: "outline, outline-offset, box-shadow" }; } const spin = css.keyframes({ "0%": { transform: "rotate(0deg)" }, "100%": { transform: "rotate(359deg)" } }); const alwaysMonoIcons = [ "grafana", "favorite", "heart-break", "heart", "panel-add", "library-panel", "circle-mono" ]; function getIconSubDir(name, type) { if (name == null ? void 0 : name.startsWith("gf-")) { return "custom"; } else if (alwaysMonoIcons.includes(name)) { return "mono"; } else if (type === "default") { return "unicons"; } else if (type === "solid") { return "solid"; } else { return "mono"; } } function getSvgSize(size) { switch (size) { case "xs": return 12; case "sm": return 14; case "md": return 16; case "lg": return 18; case "xl": return 24; case "xxl": return 36; case "xxxl": return 48; } } let iconRoot; function getIconRoot() { if (iconRoot) { return iconRoot; } const grafanaPublicPath = typeof window !== "undefined" && window.__grafana_public_path__; if (grafanaPublicPath) { iconRoot = grafanaPublicPath + "build/img/icons/"; } else { iconRoot = "public/build/img/icons/"; } return iconRoot; } function getIconPath(name, type = "default") { const iconRoot2 = getIconRoot(); const subDir = getIconSubDir(name, type); return `${iconRoot2}${subDir}/${name}.svg`; } const getIconStyles = (theme) => { return { icon: css.css({ display: "inline-block", fill: "currentColor", flexShrink: 0, label: "Icon", // line-height: 0; is needed for correct icon alignment in Safari lineHeight: 0, verticalAlign: "middle" }), orange: css.css({ fill: theme.v1.palette.orange }), spin: css.css({ [theme.transitions.handleMotion("no-preference", "reduce")]: { animation: `${spin} 2s infinite linear` } }) }; }; const Icon = React__namespace.forwardRef( ({ size = "md", type = "default", name, className, style, title = "", ...rest }, ref) => { const styles = useStyles2(getIconStyles); if (!data.isIconName(name)) { console.warn("Icon component passed an invalid icon name", name); } const iconName = name === "fa fa-spinner" ? "spinner" : name; const svgSize = getSvgSize(size); const svgHgt = svgSize; const svgWid = name.startsWith("gf-bar-align") ? 16 : name.startsWith("gf-interp") ? 30 : svgSize; const svgPath = getIconPath(iconName, type); const composedClassName = css.cx( styles.icon, className, type === "mono" ? { [styles.orange]: name === "favorite" } : "", { [styles.spin]: iconName === "spinner" } ); return /* @__PURE__ */ jsxRuntime.jsx( SVG__default.default, { "aria-hidden": rest.tabIndex === void 0 && !title && !rest["aria-label"] && !rest["aria-labelledby"] && !rest["aria-describedby"], innerRef: ref, src: svgPath, width: svgWid, height: svgHgt, title, className: composedClassName, style, loader: /* @__PURE__ */ jsxRuntime.jsx( "div", { className: css.cx( css.css({ width: svgWid, height: svgHgt }), composedClassName ) } ), ...rest } ); } ); Icon.displayName = "Icon"; const Stack = React__namespace.forwardRef((props, ref) => { const { gap = 1, rowGap, columnGap, alignItems, justifyContent, direction, wrap, children, grow, shrink, basis, flex, width, minWidth, maxWidth, height, minHeight, maxHeight, ...rest } = props; const styles = useStyles2( getStyles$H, gap, rowGap, columnGap, alignItems, justifyContent, direction, wrap, grow, shrink, basis, flex ); const sizeStyles = useStyles2(getSizeStyles, width, minWidth, maxWidth, height, minHeight, maxHeight); return /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: css.cx(styles.flex, sizeStyles), ...rest, children }); }); Stack.displayName = "Stack"; const getStyles$H = (theme, gap, rowGap, columnGap, alignItems, justifyContent, direction, wrap, grow, shrink, basis, flex) => { return { flex: css.css([ { display: "flex" }, getResponsiveStyle(theme, direction, (val) => ({ flexDirection: val })), getResponsiveStyle(theme, wrap, (val) => ({ flexWrap: typeof val === "boolean" ? val ? "wrap" : "nowrap" : val })), getResponsiveStyle(theme, alignItems, (val) => ({ alignItems: val })), getResponsiveStyle(theme, justifyContent, (val) => ({ justifyContent: val })), getResponsiveStyle(theme, gap, (val) => ({ gap: theme.spacing(val) })), getResponsiveStyle(theme, rowGap, (val) => ({ rowGap: theme.spacing(val) })), getResponsiveStyle(theme, columnGap, (val) => ({ columnGap: theme.spacing(val) })), getResponsiveStyle(theme, grow, (val) => ({ flexGrow: val })), getResponsiveStyle(theme, shrink, (val) => ({ flexShrink: val })), getResponsiveStyle(theme, basis, (val) => ({ flexBasis: val })), getResponsiveStyle(theme, flex, (val) => ({ flex: val })) ]) }; }; const modulo = (a, n) => (a % n + n) % n; const UNFOCUSED = -1; const useMenuFocus = ({ localRef, isMenuOpen, close, onOpen, onClose, onKeyDown }) => { const [focusedItem, setFocusedItem] = React.useState(UNFOCUSED); React.useEffect(() => { if (isMenuOpen) { setFocusedItem(0); } }, [isMenuOpen]); React.useEffect(() => { var _a, _b; const menuItems = (_a = localRef == null ? void 0 : localRef.current) == null ? void 0 : _a.querySelectorAll( '[data-role="menuitem"]:not([data-disabled])' ); (_b = menuItems == null ? void 0 : menuItems[focusedItem]) == null ? void 0 : _b.focus(); menuItems == null ? void 0 : menuItems.forEach((menuItem, i) => { menuItem.tabIndex = i === focusedItem ? 0 : -1; }); }, [localRef, focusedItem]); reactUse.useEffectOnce(() => { onOpen == null ? void 0 : onOpen(setFocusedItem); }); const handleKeys = (event) => { var _a, _b, _c; const menuItems = (_a = localRef == null ? void 0 : localRef.current) == null ? void 0 : _a.querySelectorAll( '[data-role="menuitem"]:not([data-disabled])' ); const menuItemsCount = (_b = menuItems == null ? void 0 : menuItems.length) != null ? _b : 0; switch (event.key) { case "ArrowUp": event.preventDefault(); event.stopPropagation(); setFocusedItem(modulo(focusedItem - 1, menuItemsCount)); break; case "ArrowDown": event.preventDefault(); event.stopPropagation(); setFocusedItem(modulo(focusedItem + 1, menuItemsCount)); break; case "ArrowLeft": event.preventDefault(); event.stopPropagation(); setFocusedItem(UNFOCUSED); close == null ? void 0 : close(); break; case "Home": event.preventDefault(); event.stopPropagation(); setFocusedItem(0); break; case "End": event.preventDefault(); event.stopPropagation(); setFocusedItem(menuItemsCount - 1); break; case "Enter": event.preventDefault(); event.stopPropagation(); (_c = menuItems == null ? void 0 : menuItems[focusedItem]) == null ? void 0 : _c.click(); break; case "Escape": onClose == null ? void 0 : onClose(); break; case "Tab": event.preventDefault(); onClose == null ? void 0 : onClose(); break; } onKeyDown == null ? void 0 : onKeyDown(event); }; return [handleKeys]; }; const isElementOverflowing = (element) => { if (!element) { return false; } const wrapperPos = element.parentElement.getBoundingClientRect(); const pos = element.getBoundingClientRect(); return pos.width !== 0 && wrapperPos.right + pos.width + 10 > window.innerWidth; }; const SubMenu = React.memo(({ items, isOpen, close, customStyle }) => { const styles = useStyles2(getStyles$G); const localRef = React.useRef(null); const [handleKeys] = useMenuFocus({ localRef, isMenuOpen: isOpen, close }); const [pushLeft, setPushLeft] = React.useState(false); React.useEffect(() => { if (isOpen && localRef.current) { setPushLeft(isElementOverflowing(localRef.current)); } }, [isOpen]); return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [ /* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.iconWrapper, "aria-hidden": true, "data-testid": e2eSelectors.selectors.components.Menu.SubMenu.icon, children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "angle-right", className: styles.icon }) }), isOpen && /* @__PURE__ */ jsxRuntime.jsx( "div", { ref: localRef, className: css.cx(styles.subMenu, { [styles.pushLeft]: pushLeft }), "data-testid": e2eSelectors.selectors.components.Menu.SubMenu.container, style: customStyle, children: /* @__PURE__ */ jsxRuntime.jsx("div", { tabIndex: -1, className: styles.itemsWrapper, role: "menu", onKeyDown: handleKeys, children: items }) } ) ] }); }); SubMenu.displayName = "SubMenu"; const getStyles$G = (theme) => { return { iconWrapper: css.css({ display: "flex", flex: 1, justifyContent: "end" }), icon: css.css({ opacity: 0.7, marginLeft: theme.spacing(1), color: theme.colors.text.secondary }), itemsWrapper: css.css({ background: theme.colors.background.elevated, padding: theme.spacing(0.5), boxShadow: theme.shadows.z3, display: "inline-block", borderRadius: theme.shape.radius.default }), pushLeft: css.css({ right: "100%", left: "unset" }), subMenu: css.css({ position: "absolute", top: 0, left: "100%", zIndex: theme.zIndex.dropdown }) }; }; const MenuItem = React__namespace.memo( React__namespace.forwardRef((props, ref) => { const { url, icon, label, description, ariaLabel, ariaChecked, target, onClick, className, active, disabled, destructive, childItems, role, tabIndex = -1, customSubMenuContainerStyles, shortcut, testId } = props; const styles = useStyles2(getStyles$F); const [isActive, setIsActive] = React.useState(active); const [isSubMenuOpen, setIsSubMenuOpen] = React.useState(false); const onMouseEnter = React.useCallback(() => { if (disabled) { return; } setIsSubMenuOpen(true); setIsActive(true); }, [disabled]); const onMouseLeave = React.useCallback(() => { if (disabled) { return; } setIsSubMenuOpen(false); setIsActive(false); }, [disabled]); const hasSubMenu = childItems && childItems.length > 0; const ItemElement = hasSubMenu ? "div" : url === void 0 ? "button" : "a"; const itemStyle = css.cx( { [styles.item]: true, [styles.active]: isActive, [styles.disabled]: disabled, [styles.destructive]: destructive && !disabled }, className ); const disabledProps = { [ItemElement === "button" ? "disabled" : "aria-disabled"]: disabled, ...ItemElement === "a" && disabled && { href: void 0, onClick: void 0 }, ...disabled && { tabIndex: -1, ["data-disabled"]: disabled // used to identify disabled items in Menu.tsx } }; const localRef = React.useRef(null); React.useImperativeHandle(ref, () => localRef.current); const handleKeys = (event) => { switch (event.key) { case "ArrowRight": event.preventDefault(); event.stopPropagation(); if (hasSubMenu) { setIsSubMenuOpen(true); setIsActive(true); } break; } }; const closeSubMenu = () => { var _a; setIsSubMenuOpen(false); setIsActive(false); (_a = localRef == null ? void 0 : localRef.current) == null ? void 0 : _a.focus(); }; const hasShortcut = Boolean(shortcut && shortcut.length > 0); return /* @__PURE__ */ jsxRuntime.jsxs( ItemElement, { target, className: itemStyle, rel: target === "_blank" ? "noopener noreferrer" : void 0, href: url, onClick: (event) => { if (hasSubMenu && !isSubMenuOpen) { event.preventDefault(); event.stopPropagation(); } onClick == null ? void 0 : onClick(event); }, onMouseEnter, onMouseLeave, onKeyDown: handleKeys, role: !url ? role || "menuitem" : role, "data-role": "menuitem", ref: localRef, "data-testid": testId, "aria-label": ariaLabel, "aria-checked": ariaChecked, tabIndex, ...disabledProps, children: [ /* @__PURE__ */ jsxRuntime.jsxs(Stack, { direction: "row", justifyContent: "flex-start", alignItems: "center", children: [ icon && /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, className: styles.icon, "aria-hidden": true }), /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.ellipsis, children: label }), /* @__PURE__ */ jsxRuntime.jsxs("div", { className: css.cx(styles.rightWrapper, { [styles.withShortcut]: hasShortcut }), children: [ hasShortcut && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.shortcut, children: [ /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "keyboard", title: i18n.t("grafana-ui.menu-item.keyboard-shortcut-label", "Keyboard shortcut") }), shortcut ] }), hasSubMenu && /* @__PURE__ */ jsxRuntime.jsx( SubMenu, { items: childItems, isOpen: isSubMenuOpen, close: closeSubMenu, customStyle: customSubMenuContainerStyles } ) ] }) ] }), description && /* @__PURE__ */ jsxRuntime.jsx( "div", { className: css.cx(styles.description, styles.ellipsis, { [styles.descriptionWithIcon]: icon !== void 0 }), children: description } ), props.component ? /* @__PURE__ */ jsxRuntime.jsx(props.component, {}) : null ] } ); }) ); MenuItem.displayName = "MenuItem"; const getStyles$F = (theme) => { return { item: css.css({ background: "none", cursor: "pointer", whiteSpace: "nowrap", color: theme.colors.text.primary, display: "flex", flexDirection: "column", alignItems: "stretch", justifyContent: "center", padding: theme.spacing(0.5, 1.5), minHeight: theme.spacing(4), borderRadius: theme.shape.radius.default, margin: 0, border: "none", width: "100%", position: "relative", "&:hover, &:focus-visible": { background: theme.colors.action.hover, color: theme.colors.text.primary, textDecoration: "none" }, "&:focus-visible": getFocusStyles(theme) }), active: css.css({ background: theme.colors.action.hover }), destructive: css.css({ color: theme.colors.error.text, svg: { color: theme.colors.error.text }, "&:hover, &:focus, &:focus-visible": { background: theme.colors.error.main, color: theme.colors.error.contrastText, svg: { color: theme.colors.error.contrastText } } }), disabled: css.css({ color: theme.colors.action.disabledText, label: "menu-item-disabled", "&:hover, &:focus, &:focus-visible": { cursor: "not-allowed", background: "none", color: theme.colors.action.disabledText } }), icon: css.css({ opacity: 0.7, color: theme.colors.text.secondary }), rightWrapper: css.css({ display: "flex", alignItems: "center", marginLeft: "auto" }), withShortcut: css.css({ minWidth: theme.spacing(10.5) }), shortcut: css.css({ display: "flex", alignItems: "center", gap: theme.spacing(1), marginLeft: theme.spacing(2), color: theme.colors.text.secondary, opacity: 0.7 }), description: css.css({ ...theme.typography.bodySmall, color: theme.colors.text.secondary, textAlign: "start" }), descriptionWithIcon: css.css({ marginLeft: theme.spacing(3) }), ellipsis: css.css({ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }) }; }; const MenuComp = React__namespace.forwardRef( ({ header, children, ariaLabel, onOpen, onClose, onKeyDown, ...otherProps }, forwardedRef) => { const styles = useStyles2(getStyles$E); const localRef = React.useRef(null); React.useImperativeHandle(forwardedRef, () => localRef.current); const [handleKeys] = useMenuFocus({ isMenuOpen: true, localRef, onOpen, onClose, onKeyDown }); return /* @__PURE__ */ jsxRuntime.jsxs( Box, { ...otherProps, "aria-label": ariaLabel, backgroundColor: "elevated", borderRadius: "default", boxShadow: "z3", display: "inline-block", onKeyDown: handleKeys, paddingX: 0.5, paddingY: 0.5, ref: localRef, role: "menu", tabIndex: -1, children: [ header && /* @__PURE__ */ jsxRuntime.jsx( "div", { className: css.cx( styles.header, Boolean(children) && React__namespace.Children.toArray(children).length > 0 && styles.headerBorder ), children: header } ), children ] } ); } ); MenuComp.displayName = "Menu"; const Menu = Object.assign(MenuComp, { Item: MenuItem, Divider: MenuDivider, Group: MenuGroup }); const getStyles$E = (theme) => { return { header: css.css({ padding: theme.spacing(0.5, 0.5, 1, 0.5) }), headerBorder: css.css({ borderBottom: `1px solid ${theme.colors.border.weak}`, marginBottom: theme.spacing(0.5) }) }; }; function Portal(props) { const { children, className, root, forwardedRef } = props; const theme = useTheme2(); const node = React.useRef(null); const portalRoot = root != null ? root : getPortalContainer(); if (!node.current) { node.current = document.createElement("div"); if (className) { node.current.className = className; } node.current.style.position = "relative"; node.current.style.zIndex = `${theme.zIndex.portal}`; } React.useLayoutEffect(() => { if (node.current) { portalRoot.appendChild(node.current); } return () => { if (node.current) { portalRoot.removeChild(node.current); } }; }, [portalRoot]); return ReactDOM__default.default.createPortal(/* @__PURE__ */ jsxRuntime.jsx("div", { ref: forwardedRef, children }), node.current); } function getPortalContainer() { var _a; return (_a = window.document.getElementById("grafana-portal-container")) != null ? _a : document.body; } const RefForwardingPortal = React__namespace.forwardRef((props, ref) => { return /* @__PURE__ */ jsxRuntime.jsx(Portal, { ...props, forwardedRef: ref }); }); RefForwardingPortal.displayName = "RefForwardingPortal"; const ContextMenu = React__namespace.memo( ({ x, y, onClose, focusOnOpen = true, renderMenuItems, renderHeader }) => { const menuRef = React.useRef(null); const [positionStyles, setPositionStyles] = React.useState({}); React.useLayoutEffect(() => { const menuElement = menuRef.current; if (menuElement) { const rect = menuElement.getBoundingClientRect(); const OFFSET = 5; const collisions = { right: window.innerWidth < x + rect.width, bottom: window.innerHeight < y + rect.height + OFFSET }; setPositionStyles({ position: "fixed", left: collisions.right ? x - rect.width - OFFSET : x - OFFSET, top: Math.max(0, collisions.bottom ? y - rect.height - OFFSET : y + OFFSET) }); } }, [x, y]); reactUse.useClickAway(menuRef, () => { onClose == null ? void 0 : onClose(); }); const header = renderHeader == null ? void 0 : renderHeader(); const menuItems = renderMenuItems == null ? void 0 : renderMenuItems(); const onOpen = (setFocusedItem) => { if (focusOnOpen) { setFocusedItem(0); } }; const onKeyDown = (e) => { if (e.key === "Escape") { e.preventDefault(); e.stopPropagation(); onClose == null ? void 0 : onClose(); } }; return /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsx( Menu, { header, ref: menuRef, style: positionStyles, ariaLabel: e2eSelectors.selectors.components.Menu.MenuComponent("Context"), onOpen, onClick: onClose, onKeyDown, children: menuItems } ) }); } ); ContextMenu.displayName = "ContextMenu"; const getFocusStyle = (theme) => css.css({ "&:focus": getFocusStyles(theme) }); const sharedInputStyle = (theme, invalid = false) => { const borderColor = invalid ? theme.colors.error.border : theme.components.input.borderColor; const borderColorHover = invalid ? theme.colors.error.shade : theme.components.input.borderHover; const background = theme.components.input.background; const textColor = theme.components.input.text; const autoFillBorder = theme.isDark ? "#2e2f35" : "#bab4ca"; return css.cx( inputPadding(theme), css.css({ background, lineHeight: theme.typography.body.lineHeight, fontSize: theme.typography.size.md, color: textColor, border: `1px solid ${borderColor}`, "&:-webkit-autofill, &:-webkit-autofill:hover": { /* Welcome to 2005. This is a HACK to get rid od Chromes default autofill styling */ boxShadow: `inset 0 0 0 1px rgba(255, 255, 255, 0), inset 0 0 0 100px ${background}!important`, WebkitTextFillColor: `${textColor} !important`, borderColor: autoFillBorder }, "&:-webkit-autofill:focus": { /* Welcome to 2005. This is a HACK to get rid od Chromes default autofill styling */ boxShadow: `0 0 0 2px ${theme.colors.background.primary}, 0 0 0px 4px ${theme.colors.primary.main}, inset 0 0 0 1px rgba(255, 255, 255, 0), inset 0 0 0 100px ${background}!important`, WebkitTextFillColor: `${textColor} !important` }, "&:hover": { borderColor: borderColorHover }, "&:focus": { outline: "none" }, "&:disabled": { backgroundColor: theme.colors.action.disabledBackground, color: theme.colors.action.disabledText, border: `1px solid ${theme.colors.action.disabledBackground}`, "&:hover": { borderColor } }, "&::placeholder": { color: theme.colors.text.disabled, opacity: 1 } }) ); }; const inputPadding = (theme) => { return css.css({ padding: theme.spacing(0, 1, 0, 1) }); }; function getPropertiesForButtonSize(size, theme) { switch (size) { case "sm": return { padding: 1, fontSize: theme.typography.size.sm, height: theme.components.height.sm }; case "lg": return { padding: 3, fontSize: theme.typography.size.lg, height: theme.components.height.lg }; case "md": default: return { padding: 2, fontSize: theme.typography.size.md, height: theme.components.height.md }; } } function getPlacement(placement) { switch (placement) { case "auto": return "bottom"; case "auto-start": return "bottom-start"; case "auto-end": return "bottom-end"; default: return placement != null ? placement : "bottom"; } } function buildTooltipTheme(theme, tooltipBg, toggletipBorder, tooltipText, tooltipPadding) { return { arrow: css.css({ fill: tooltipBg }), container: css.css({ backgroundColor: tooltipBg, borderRadius: theme.shape.radius.default, border: `1px solid ${toggletipBorder}`, boxShadow: theme.shadows.z2, color: tooltipText, fontSize: theme.typography.bodySmall.fontSize, padding: theme.spacing(tooltipPadding.topBottom, tooltipPadding.rightLeft), [theme.transitions.handleMotion("no-preference", "reduce")]: { transition: "opacity 0.3s" }, zIndex: theme.zIndex.tooltip, maxWidth: "400px", overflowWrap: "break-word", "&[data-popper-interactive='false']": { pointerEvents: "none" } }), headerClose: css.css({ color: theme.colors.text.secondary, position: "absolute", right: theme.spacing(1), top: theme.spacing(1.5), backgroundColor: "transparent", border: 0 }), header: css.css({ paddingTop: theme.spacing(1), paddingBottom: theme.spacing(2) }), body: css.css({ paddingTop: theme.spacing(1), paddingBottom: theme.spacing(1) }), footer: css.css({ paddingTop: theme.spacing(2), paddingBottom: theme.spacing(1) }) }; } const Tooltip = React.forwardRef( ({ children, theme, interactive, show, placement, content }, forwardedRef) => { const arrowRef = React.useRef(null); const [controlledVisible, setControlledVisible] = React.useState(show); const isOpen = show != null ? show : controlledVisible; const middleware = [ react.offset(8), react.flip({ fallbackAxisSideDirection: "end", // see https://floating-ui.com/docs/flip#combining-with-shift crossAxis: false, boundary: document.body }), react.shift(), react.arrow({ element: arrowRef }) ]; const { context, refs, floatingStyles } = react.useFloating({ open: isOpen, placement: getPlacement(placement), onOpenChange: setControlledVisible, middleware, whileElementsMounted: react.autoUpdate }); const tooltipId = React.useId(); const hover = react.useHover(context, { handleClose: interactive ? react.safePolygon() : void 0, move: false }); const focus = react.useFocus(context); const dismiss = react.useDismiss(context); const { getReferenceProps, getFloatingProps } = react.useInteractions([dismiss, hover, focus]); const contentIsFunction = typeof content === "function"; const styles = useStyles2(getStyles$D); const style = styles[theme != null ? theme : "info"]; const handleRef = React.useCallback( (ref) => { refs.setReference(ref); if (typeof forwardedRef === "function") { forwardedRef(ref); } else if (forwardedRef) { forwardedRef.current = ref; } }, [forwardedRef, refs] ); const childHasMatchingAriaLabel = "aria-label" in children.props && children.props["aria-label"] === content; return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [ React.cloneElement(children, { ref: handleRef, tabIndex: 0, // tooltip trigger should be keyboard focusable "aria-describedby": !childHasMatchingAriaLabel && isOpen ? tooltipId : void 0, ...getReferenceProps() }), isOpen && /* @__PURE__ */ jsxRuntime.jsx(Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: refs.setFloating, style: floatingStyles, ...getFloatingProps(), children: [ /* @__PURE__ */ jsxRuntime.jsx(react.FloatingArrow, { className: style.arrow, ref: arrowRef, context }), /* @__PURE__ */ jsxRuntime.jsxs( "div", { "data-testid": e2eSelectors.selectors.components.Tooltip.container, id: tooltipId, role: "tooltip", className: style.container, children: [ typeof content === "string" && content, React.isValidElement(content) && React.cloneElement(content), contentIsFunction && content({}) ] } ) ] }) }) ] }); } ); Tooltip.displayName = "Tooltip"; const getStyles$D = (theme) => { const info = buildTooltipTheme( theme, theme.components.tooltip.background, theme.components.tooltip.background, theme.components.tooltip.text, { topBottom: 0.5, rightLeft: 1 } ); const error = buildTooltipTheme( theme, theme.colors.error.main, theme.colors.error.main, theme.colors.error.contrastText, { topBottom: 0.5, rightLeft: 1 } ); return { info, ["info-alt"]: info, error }; }; const Button = React__namespace.forwardRef( ({ variant = "primary", size = "md", fill = "solid", icon, fullWidth, children, className, type = "button", tooltip, disabled, tooltipPlacement, iconPlacement = "left", onClick, ...otherProps }, ref) => { const theme = useTheme2(); const styles = getButtonStyles({ theme, size, variant, fill, fullWidth, iconOnly: !children }); const buttonStyles = css.cx( styles.button, { [styles.disabled]: disabled }, className ); const hasTooltip = Boolean(tooltip); const iconComponent = icon && /* @__PURE__ */ jsxRuntime.jsx(IconRenderer, { icon, size, className: styles.icon }); const button = /* @__PURE__ */ jsxRuntime.jsxs( "button", { className: buttonStyles, type, onClick: disabled ? void 0 : onClick, ...otherProps, "aria-disabled": hasTooltip && disabled, disabled: !hasTooltip && disabled, ref: tooltip ? void 0 : ref, children: [ iconPlacement === "left" && iconComponent, children && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.content, children }), iconPlacement === "right" && iconComponent ] } ); if (tooltip) { return /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { ref, content: tooltip, placement: tooltipPlacement, children: button }); } return button; } ); Button.displayName = "Button"; const LinkButton = React__namespace.forwardRef( ({ variant = "primary", size = "md", fill = "solid", icon, fullWidth, children, className, onBlur, onFocus, disabled, tooltip, tooltipPlacement, ...otherProps }, ref) => { const theme = useTheme2(); const styles = getButtonStyles({ theme, fullWidth, size, variant, fill, iconOnly: !children }); const linkButtonStyles = css.cx( styles.button, { [css.css(styles.disabled, { pointerEvents: "none" })]: disabled }, className ); const button = /* @__PURE__ */ jsxRuntime.jsxs( "a", { className: linkButtonStyles, ...otherProps, tabIndex: disabled ? -1 : 0, "aria-disabled": disabled, ref: tooltip ? void 0 : ref, children: [ /* @__PURE__ */ jsxRuntime.jsx(IconRenderer, { icon, size, className: styles.icon }), children && /* @__PURE__ */ jsxRuntime.jsx("span", { className: styles.content, children }) ] } ); if (tooltip) { return /* @__PURE__ */ jsxRuntime.jsx(Tooltip, { ref, content: tooltip, placement: tooltipPlacement, children: button }); } return button; } ); LinkButton.displayName = "LinkButton"; const IconRenderer = ({ icon, size, className, iconType }) => { if (!icon) { return null; } if (React__namespace.isValidElement(icon)) { return React__namespace.cloneElement(icon, { className, size }); } return /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, size, className, type: iconType }); }; const getButtonStyles = (props) => { const { theme, variant, fill = "solid", size, iconOnly, fullWidth } = props; const { height, padding, fontSize } = getPropertiesForButtonSize(size, theme); const variantStyles = getPropertiesForVariant(theme, variant, fill); const disabledStyles = getPropertiesForDisabled(theme, variant, fill); const focusStyle = getFocusStyles(theme); const paddingMinusBorder = theme.spacing.gridSize * padding - 1; return { button: css.css({ label: "button", display: "inline-flex", alignItems: "center", gap: theme.spacing(1), fontSize, fontWeight: theme.typography.fontWeightMedium, fontFamily: theme.typography.fontFamily, padding: `0 ${paddingMinusBorder}px`, height: theme.spacing(height), // Deduct border from line-height for perfect vertical centering on windows and linux lineHeight: `${theme.spacing.gridSize * height - 2}px`, verticalAlign: "middle", cursor: "pointer", borderRadius: theme.shape.radius.default, "&:focus": focusStyle, "&:focus-visible": focusStyle, "&:focus:not(:focus-visible)": getMouseFocusStyles(), ...fullWidth && { flexGrow: 1, justifyContent: "center" }, ...variantStyles, ":disabled": disabledStyles, "&[disabled]": disabledStyles }), disabled: css.css(disabledStyles, { "&:hover": css.css(disabledStyles) }), img: css.css({ width: "16px", height: "16px", margin: theme.spacing(0, 1, 0, 0.5) }), icon: iconOnly ? css.css({ // Important not to set margin bottom here as it would override internal icon bottom margin marginRight: theme.spacing(-padding / 2), marginLeft: theme.spacing(-padding / 2) }) : void 0, content: css.css({ display: "flex", flexDirection: "row", alignItems: "center", whiteSpace: "nowrap", overflow: "hidden", height: "100%" }) }; }; function getButtonVariantStyles(theme, color, fill) { let outlineBorderColor = color.border; let borderColor = "transparent"; let hoverBorderColor = "transparent"; if (color.name === "secondary") { borderColor = color.border; hoverBorderColor = theme.colors.emphasize(color.border, 0.25); outlineBorderColor = theme.colors.border.strong; } if (fill === "outline") { return { background: "transparent", color: color.text, border: `1px solid ${outlineBorderColor}`, transition: theme.transitions.create(["background-color", "border-color", "color"], { duration: theme.transitions.duration.short }), "&:hover": { background: color.transparent, borderColor: theme.colors.emphasize(outlineBorderColor, 0.25), color: color.text } }; } if (fill === "text") { return { background: "transparent", color: color.text, border: "1px solid transparent", transition: theme.transitions.create(["background-color", "color"], { duration: theme.transitions.duration.short }), "&:focus": { outline: "none", textDecoration: "none" }, "&:hover": { background: color.transparent, textDecoration: "none" } }; } return { background: color.main, color: color.contrastText, border: `1px solid ${borderColor}`, transition: theme.transitions.create(["background-color", "box-shadow", "border-color", "color"], { duration: theme.transitions.duration.short }), "&:hover": { background: color.shade, color: color.contrastText, boxShadow: theme.shadows.z1, borderColor: hoverBorderColor } }; } function getPropertiesForDisabled(theme, variant, fill) { const disabledStyles = { cursor: "not-allowed", boxShadow: "none", color: theme.colors.text.disabled, transition: "none" }; if (fill === "text") { return { ...disabledStyles, background: "transparent", border: `1px solid transparent` }; } if (fill === "outline") { return { ...disabledStyles, background: "transparent", border: `1px solid ${theme.colors.border.weak}` }; } return { ...disabledStyles,