mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 09:05:45 -06:00
* Bind command palette specific overrides and reset when no longer relevant * Use original global escape instead of resetting the whole keybinding profile
154 lines
4.1 KiB
TypeScript
154 lines
4.1 KiB
TypeScript
import { css } from '@emotion/css';
|
|
import {
|
|
KBarAnimator,
|
|
KBarPortal,
|
|
KBarPositioner,
|
|
KBarResults,
|
|
KBarSearch,
|
|
useMatches,
|
|
Action,
|
|
VisualState,
|
|
useRegisterActions,
|
|
useKBar,
|
|
} from 'kbar';
|
|
import React, { useEffect, useState } from 'react';
|
|
import { useSelector } from 'react-redux';
|
|
|
|
import { GrafanaTheme2 } from '@grafana/data';
|
|
import { reportInteraction, locationService } from '@grafana/runtime';
|
|
import { useStyles2 } from '@grafana/ui';
|
|
import { StoreState } from 'app/types';
|
|
|
|
import { keybindingSrv } from '../../core/services/keybindingSrv';
|
|
|
|
import { ResultItem } from './ResultItem';
|
|
import getDashboardNavActions from './actions/dashboard.nav.actions';
|
|
import getGlobalActions from './actions/global.static.actions';
|
|
|
|
/**
|
|
* Wrap all the components from KBar here.
|
|
* @constructor
|
|
*/
|
|
|
|
export const CommandPalette = () => {
|
|
const styles = useStyles2(getSearchStyles);
|
|
const [actions, setActions] = useState<Action[]>([]);
|
|
const { query, showing } = useKBar((state) => ({
|
|
showing: state.visualState === VisualState.showing,
|
|
}));
|
|
const isNotLogin = locationService.getLocation().pathname !== '/login';
|
|
|
|
const { navBarTree } = useSelector((state: StoreState) => {
|
|
return {
|
|
navBarTree: state.navBarTree,
|
|
};
|
|
});
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
if (isNotLogin) {
|
|
const staticActions = getGlobalActions(navBarTree);
|
|
const dashAct = await getDashboardNavActions('go/dashboard');
|
|
setActions([...staticActions, ...dashAct]);
|
|
}
|
|
})();
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [isNotLogin]);
|
|
|
|
useEffect(() => {
|
|
if (showing) {
|
|
reportInteraction('commandPalette_opened');
|
|
|
|
keybindingSrv.bindGlobal('esc', () => {
|
|
query.setVisualState(VisualState.animatingOut);
|
|
});
|
|
}
|
|
|
|
return () => {
|
|
keybindingSrv.bindGlobal('esc', () => {
|
|
keybindingSrv.globalEsc();
|
|
});
|
|
};
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [showing]);
|
|
|
|
useRegisterActions(actions, [actions]);
|
|
|
|
return actions.length > 0 ? (
|
|
<KBarPortal>
|
|
<KBarPositioner className={styles.positioner}>
|
|
<KBarAnimator className={styles.animator}>
|
|
<KBarSearch className={styles.search} />
|
|
<RenderResults />
|
|
</KBarAnimator>
|
|
</KBarPositioner>
|
|
</KBarPortal>
|
|
) : null;
|
|
};
|
|
|
|
const RenderResults = () => {
|
|
const { results, rootActionId } = useMatches();
|
|
const styles = useStyles2(getSearchStyles);
|
|
|
|
return (
|
|
<div className={styles.resultsContainer}>
|
|
<KBarResults
|
|
items={results}
|
|
onRender={({ item, active }) =>
|
|
typeof item === 'string' ? (
|
|
<div className={styles.sectionHeader}>{item}</div>
|
|
) : (
|
|
<ResultItem action={item} active={active} currentRootActionId={rootActionId!} />
|
|
)
|
|
}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const getSearchStyles = (theme: GrafanaTheme2) => ({
|
|
positioner: css({
|
|
zIndex: theme.zIndex.portal,
|
|
marginTop: '0px',
|
|
'&::before': {
|
|
content: '""',
|
|
position: 'fixed',
|
|
top: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
left: 0,
|
|
background: theme.components.overlay.background,
|
|
backdropFilter: 'blur(1px)',
|
|
},
|
|
}),
|
|
animator: css({
|
|
maxWidth: theme.breakpoints.values.sm, // supposed to be 600...
|
|
width: '100%',
|
|
background: theme.colors.background.canvas,
|
|
color: theme.colors.text.primary,
|
|
borderRadius: theme.shape.borderRadius(4),
|
|
overflow: 'hidden',
|
|
boxShadow: theme.shadows.z3,
|
|
}),
|
|
search: css({
|
|
padding: theme.spacing(2, 3),
|
|
fontSize: theme.typography.fontSize,
|
|
width: '100%',
|
|
boxSizing: 'border-box',
|
|
outline: 'none',
|
|
border: 'none',
|
|
background: theme.colors.background.canvas,
|
|
color: theme.colors.text.primary,
|
|
borderBottom: `1px solid ${theme.colors.border.weak}`,
|
|
}),
|
|
sectionHeader: css({
|
|
padding: theme.spacing(1, 2),
|
|
fontSize: theme.typography.h6.fontSize,
|
|
fontWeight: theme.typography.body.fontWeight,
|
|
color: theme.colors.text.secondary,
|
|
}),
|
|
resultsContainer: css({
|
|
padding: theme.spacing(2, 0),
|
|
}),
|
|
});
|