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