UNPKG

@grafana/flamegraph

Version:

Grafana flamegraph visualization component

386 lines (383 loc) • 13 kB
import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { cx, css } from '@emotion/css'; import { useState, useEffect } from 'react'; import { usePrevious, useDebounce } from 'react-use'; import { OpenAssistantButton } from '@grafana/assistant'; import { useStyles2, Button, Input, RadioButtonGroup, IconButton, Dropdown, Menu, ButtonGroup } from '@grafana/ui'; import { ColorSchemeButton } from './ColorSchemeButton.mjs'; import { MIN_WIDTH_TO_SHOW_SPLIT_PANE_SELECTORS, MIN_WIDTH_TO_SHOW_BOTH_TOPTABLE_AND_FLAMEGRAPH } from './constants.mjs'; import { ViewMode, PaneView, SelectedView } from './types.mjs'; "use strict"; function isNewUI(props) { return "enableNewUI" in props && props.enableNewUI === true; } const FlameGraphHeader = (props) => { var _a, _b, _c; const styles = useStyles2(getStyles); const [localSearch, setLocalSearch] = useSearchInput(props.search, props.setSearch); const suffix = localSearch !== "" ? /* @__PURE__ */ jsx( Button, { icon: "times", fill: "text", size: "sm", onClick: () => { props.setSearch(""); setLocalSearch(""); }, children: "Clear" } ) : null; if (isNewUI(props)) { const effectiveViewMode = props.canShowSplitView ? props.viewMode : ViewMode.Single; return /* @__PURE__ */ jsxs("div", { className: cx(styles.header, styles.headerNew, { [styles.stickyHeader]: props.stickyHeader }), children: [ /* @__PURE__ */ jsx("div", { className: styles.inputContainerNew, children: /* @__PURE__ */ jsx( Input, { value: localSearch || "", onChange: (v) => { setLocalSearch(v.currentTarget.value); }, placeholder: "Search...", suffix } ) }), effectiveViewMode === ViewMode.Split && /* @__PURE__ */ jsx("div", { className: styles.middleContainer, children: props.containerWidth >= MIN_WIDTH_TO_SHOW_SPLIT_PANE_SELECTORS ? /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx( RadioButtonGroup, { size: "sm", options: paneViewOptions, value: props.leftPaneView, onChange: props.setLeftPaneView, className: styles.buttonSpacing } ), /* @__PURE__ */ jsx(IconButton, { name: "exchange-alt", size: "sm", tooltip: "Swap views", onClick: props.onSwapPanes }), /* @__PURE__ */ jsx( RadioButtonGroup, { size: "sm", options: paneViewOptions, value: props.rightPaneView, onChange: props.setRightPaneView, className: styles.buttonSpacing } ) ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx( Dropdown, { overlay: /* @__PURE__ */ jsx(Menu, { children: paneViewOptions.map((option) => { var _a2; return /* @__PURE__ */ jsx( Menu.Item, { label: (_a2 = option.label) != null ? _a2 : "", active: props.leftPaneView === option.value, onClick: () => option.value && props.setLeftPaneView(option.value) }, option.value ); }) }), children: /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", className: styles.paneDropdownButton, children: (_a = paneViewOptions.find((o) => o.value === props.leftPaneView)) == null ? void 0 : _a.label }) } ), /* @__PURE__ */ jsx(IconButton, { name: "exchange-alt", size: "sm", tooltip: "Swap views", onClick: props.onSwapPanes }), /* @__PURE__ */ jsx( Dropdown, { overlay: /* @__PURE__ */ jsx(Menu, { children: paneViewOptions.map((option) => { var _a2; return /* @__PURE__ */ jsx( Menu.Item, { label: (_a2 = option.label) != null ? _a2 : "", active: props.rightPaneView === option.value, onClick: () => option.value && props.setRightPaneView(option.value) }, option.value ); }) }), children: /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", className: styles.paneDropdownButton, children: (_b = paneViewOptions.find((o) => o.value === props.rightPaneView)) == null ? void 0 : _b.label }) } ) ] }) }), /* @__PURE__ */ jsxs("div", { className: styles.rightContainer, children: [ !!((_c = props.assistantContext) == null ? void 0 : _c.length) && /* @__PURE__ */ jsx("div", { className: styles.buttonSpacing, children: /* @__PURE__ */ jsx( OpenAssistantButton, { origin: "grafana/flame-graph", prompt: "Analyze this flamegraph by querying the current datasource", context: props.assistantContext } ) }), props.showResetButton && /* @__PURE__ */ jsx( Button, { variant: "secondary", fill: "outline", size: "sm", icon: "history-alt", tooltip: "Reset focus and sandwich state", onClick: () => { props.onReset(); }, className: styles.buttonSpacing, "aria-label": "Reset focus and sandwich state" } ), effectiveViewMode === ViewMode.Single && /* @__PURE__ */ jsx( RadioButtonGroup, { size: "sm", options: paneViewOptions, value: props.singleView, onChange: props.setSingleView, className: styles.buttonSpacing } ), props.canShowSplitView && /* @__PURE__ */ jsx( RadioButtonGroup, { size: "sm", options: viewModeOptions, value: props.viewMode, onChange: props.setViewMode, className: styles.buttonSpacing } ), props.extraHeaderElements && /* @__PURE__ */ jsx("div", { className: styles.extraElements, children: props.extraHeaderElements }) ] }) ] }); } const { selectedView, setSelectedView, containerWidth, onReset, textAlign, onTextAlignChange, showResetButton, colorScheme, onColorSchemeChange, stickyHeader, extraHeaderElements, vertical, isDiffMode, setCollapsedMap, collapsedMap, assistantContext } = props; return /* @__PURE__ */ jsxs("div", { className: cx(styles.header, { [styles.stickyHeader]: stickyHeader }), children: [ /* @__PURE__ */ jsx("div", { className: styles.inputContainer, children: /* @__PURE__ */ jsx( Input, { value: localSearch || "", onChange: (v) => { setLocalSearch(v.currentTarget.value); }, placeholder: "Search...", suffix } ) }), /* @__PURE__ */ jsxs("div", { className: styles.rightContainer, children: [ !!(assistantContext == null ? void 0 : assistantContext.length) && /* @__PURE__ */ jsx("div", { className: styles.buttonSpacing, children: /* @__PURE__ */ jsx( OpenAssistantButton, { origin: "grafana/flame-graph", prompt: "Analyze this flamegraph by querying the current datasource", context: assistantContext } ) }), showResetButton && /* @__PURE__ */ jsx( Button, { variant: "secondary", fill: "outline", size: "sm", icon: "history-alt", tooltip: "Reset focus and sandwich state", onClick: () => { onReset(); }, className: styles.buttonSpacing, "aria-label": "Reset focus and sandwich state" } ), /* @__PURE__ */ jsx(ColorSchemeButton, { value: colorScheme, onChange: onColorSchemeChange, isDiffMode }), /* @__PURE__ */ jsxs(ButtonGroup, { className: styles.buttonSpacing, children: [ /* @__PURE__ */ jsx( Button, { variant: "secondary", fill: "outline", size: "sm", tooltip: "Expand all groups", onClick: () => { setCollapsedMap(collapsedMap.setAllCollapsedStatus(false)); }, "aria-label": "Expand all groups", icon: "angle-double-down", disabled: selectedView === SelectedView.TopTable } ), /* @__PURE__ */ jsx( Button, { variant: "secondary", fill: "outline", size: "sm", tooltip: "Collapse all groups", onClick: () => { setCollapsedMap(collapsedMap.setAllCollapsedStatus(true)); }, "aria-label": "Collapse all groups", icon: "angle-double-up", disabled: selectedView === SelectedView.TopTable } ) ] }), /* @__PURE__ */ jsx( RadioButtonGroup, { size: "sm", disabled: selectedView === SelectedView.TopTable, options: alignOptions, value: textAlign, onChange: onTextAlignChange, className: styles.buttonSpacing } ), /* @__PURE__ */ jsx( RadioButtonGroup, { size: "sm", options: getViewOptions(containerWidth, vertical), value: selectedView, onChange: setSelectedView } ), extraHeaderElements && /* @__PURE__ */ jsx("div", { className: styles.extraElements, children: extraHeaderElements }) ] }) ] }); }; const alignOptions = [ { value: "left", description: "Align text left", icon: "align-left" }, { value: "right", description: "Align text right", icon: "align-right" } ]; const viewModeOptions = [ { value: ViewMode.Single, label: "Single", description: "Single view" }, { value: ViewMode.Split, label: "Split", description: "Split view" } ]; const paneViewOptions = [ { value: PaneView.TopTable, label: "Top Table" }, { value: PaneView.FlameGraph, label: "Flame Graph" }, { value: PaneView.CallTree, label: "Call Tree" } ]; function getViewOptions(width, vertical) { let viewOptions = [ { value: SelectedView.TopTable, label: "Top Table", description: "Only show top table" }, { value: SelectedView.FlameGraph, label: "Flame Graph", description: "Only show flame graph" } ]; if (width >= MIN_WIDTH_TO_SHOW_BOTH_TOPTABLE_AND_FLAMEGRAPH || vertical) { viewOptions.push({ value: SelectedView.Both, label: "Both", description: "Show both the top table and flame graph" }); } return viewOptions; } function useSearchInput(search, setSearch) { const [localSearchState, setLocalSearchState] = useState(search); const prevSearch = usePrevious(search); useDebounce( () => { setSearch(localSearchState); }, 250, [localSearchState] ); useEffect(() => { if (prevSearch !== search && search !== localSearchState) { setLocalSearchState(search); } }, [search, prevSearch, localSearchState]); return [localSearchState, setLocalSearchState]; } const getStyles = (theme) => ({ header: css({ label: "header", display: "flex", flexWrap: "wrap", justifyContent: "space-between", width: "100%", top: 0, gap: theme.spacing(1), marginTop: theme.spacing(1) }), headerNew: css({ label: "headerNew", alignItems: "flex-start", position: "relative" }), stickyHeader: css({ zIndex: theme.zIndex.navbarFixed, position: "sticky", background: theme.colors.background.primary }), inputContainer: css({ label: "inputContainer", flexGrow: 1, minWidth: "150px", maxWidth: "350px" }), inputContainerNew: css({ label: "inputContainerNew", flexGrow: 0, minWidth: "150px", maxWidth: "350px" }), middleContainer: css({ label: "middleContainer", display: "flex", alignItems: "center", flexWrap: "wrap", gap: theme.spacing(1), position: "absolute", left: "50%", transform: "translateX(-50%)" }), rightContainer: css({ label: "rightContainer", display: "flex", alignItems: "flex-start", flexWrap: "wrap" }), buttonSpacing: css({ label: "buttonSpacing", marginRight: theme.spacing(1) }), resetButton: css({ label: "resetButton", display: "flex", marginRight: theme.spacing(2) }), resetButtonIconWrapper: css({ label: "resetButtonIcon", padding: "0 5px", color: theme.colors.text.disabled }), extraElements: css({ label: "extraElements", marginLeft: theme.spacing(1) }), paneDropdownButton: css({ label: "paneDropdownButton", minWidth: "95px", justifyContent: "center" }) }); export { alignOptions, FlameGraphHeader as default }; //# sourceMappingURL=FlameGraphHeader.mjs.map